mirror of
https://github.com/nushell/nushell
synced 2024-12-29 06:23:11 +00:00
0c4d5330ee
# Description This breaks `nu-plugin` up into four crates: - `nu-plugin-protocol`: just the type definitions for the protocol, no I/O. If someone wanted to wire up something more bare metal, maybe for async I/O, they could use this. - `nu-plugin-core`: the shared stuff between engine/plugin. Less stable interface. - `nu-plugin-engine`: everything required for the engine to talk to plugins. Less stable interface. - `nu-plugin`: everything required for the plugin to talk to the engine, what plugin developers use. Should be the most stable interface. No changes are made to the interface exposed by `nu-plugin` - it should all still be there. Re-exports from `nu-plugin-protocol` or `nu-plugin-core` are used as required. Plugins shouldn't ever have to use those crates directly. This should be somewhat faster to compile as `nu-plugin-engine` and `nu-plugin` can compile in parallel, and the engine doesn't need `nu-plugin` and plugins don't need `nu-plugin-engine` (except for test support), so that should reduce what needs to be compiled too. The only significant change here other than splitting stuff up was to break the `source` out of `PluginCustomValue` and create a new `PluginCustomValueWithSource` type that contains that instead. One bonus of that is we get rid of the option and it's now more type-safe, but it also means that the logic for that stuff (actually running the plugin for custom value ops) can live entirely within the `nu-plugin-engine` crate. # User-Facing Changes - New crates. - Added `local-socket` feature for `nu` to try to make it possible to compile without that support if needed. # Tests + Formatting - 🟢 `toolkit fmt` - 🟢 `toolkit clippy` - 🟢 `toolkit test` - 🟢 `toolkit test stdlib`
106 lines
4 KiB
Rust
106 lines
4 KiB
Rust
use nu_protocol::ShellError;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
/// Protocol information, sent as a `Hello` message on initialization. This determines the
|
|
/// compatibility of the plugin and engine. They are considered to be compatible if the lower
|
|
/// version is semver compatible with the higher one.
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
pub struct ProtocolInfo {
|
|
/// The name of the protocol being implemented. Only one protocol is supported. This field
|
|
/// can be safely ignored, because not matching is a deserialization error
|
|
pub protocol: Protocol,
|
|
/// The semantic version of the protocol. This should be the version of the `nu-plugin-protocol`
|
|
/// crate
|
|
pub version: String,
|
|
/// Supported optional features. This helps to maintain semver compatibility when adding new
|
|
/// features
|
|
pub features: Vec<Feature>,
|
|
}
|
|
|
|
impl Default for ProtocolInfo {
|
|
fn default() -> ProtocolInfo {
|
|
ProtocolInfo {
|
|
protocol: Protocol::NuPlugin,
|
|
version: env!("CARGO_PKG_VERSION").into(),
|
|
features: default_features(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ProtocolInfo {
|
|
/// True if the version specified in `self` is compatible with the version specified in `other`.
|
|
pub fn is_compatible_with(&self, other: &ProtocolInfo) -> Result<bool, ShellError> {
|
|
fn parse_failed(error: semver::Error) -> ShellError {
|
|
ShellError::PluginFailedToLoad {
|
|
msg: format!("Failed to parse protocol version: {error}"),
|
|
}
|
|
}
|
|
let mut versions = [
|
|
semver::Version::parse(&self.version).map_err(parse_failed)?,
|
|
semver::Version::parse(&other.version).map_err(parse_failed)?,
|
|
];
|
|
|
|
versions.sort();
|
|
|
|
// For example, if the lower version is 1.1.0, and the higher version is 1.2.3, the
|
|
// requirement is that 1.2.3 matches ^1.1.0 (which it does)
|
|
Ok(semver::Comparator {
|
|
op: semver::Op::Caret,
|
|
major: versions[0].major,
|
|
minor: Some(versions[0].minor),
|
|
patch: Some(versions[0].patch),
|
|
pre: versions[0].pre.clone(),
|
|
}
|
|
.matches(&versions[1]))
|
|
}
|
|
|
|
/// True if the protocol info contains a feature compatible with the given feature.
|
|
pub fn supports_feature(&self, feature: &Feature) -> bool {
|
|
self.features.iter().any(|f| feature.is_compatible_with(f))
|
|
}
|
|
}
|
|
|
|
/// Indicates the protocol in use. Only one protocol is supported.
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
|
pub enum Protocol {
|
|
/// Serializes to the value `"nu-plugin"`
|
|
#[serde(rename = "nu-plugin")]
|
|
#[default]
|
|
NuPlugin,
|
|
}
|
|
|
|
/// Indicates optional protocol features. This can help to make non-breaking-change additions to
|
|
/// the protocol. Features are not restricted to plain strings and can contain additional
|
|
/// configuration data.
|
|
///
|
|
/// Optional features should not be used by the protocol if they are not present in the
|
|
/// [`ProtocolInfo`] sent by the other side.
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
#[serde(tag = "name")]
|
|
pub enum Feature {
|
|
/// The plugin supports running with a local socket passed via `--local-socket` instead of
|
|
/// stdio.
|
|
LocalSocket,
|
|
|
|
/// A feature that was not recognized on deserialization. Attempting to serialize this feature
|
|
/// is an error. Matching against it may only be used if necessary to determine whether
|
|
/// unsupported features are present.
|
|
#[serde(other, skip_serializing)]
|
|
Unknown,
|
|
}
|
|
|
|
impl Feature {
|
|
/// True if the feature is considered to be compatible with another feature.
|
|
pub fn is_compatible_with(&self, other: &Feature) -> bool {
|
|
matches!((self, other), (Feature::LocalSocket, Feature::LocalSocket))
|
|
}
|
|
}
|
|
|
|
/// Protocol features compiled into this version of `nu-plugin`.
|
|
pub fn default_features() -> Vec<Feature> {
|
|
vec![
|
|
// Only available if compiled with the `local-socket` feature flag (enabled by default).
|
|
#[cfg(feature = "local-socket")]
|
|
Feature::LocalSocket,
|
|
]
|
|
}
|