use nu_protocol::ShellError;
use semver::Prerelease;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ProtocolInfo {
pub protocol: Protocol,
pub version: String,
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 {
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();
versions[1].pre = Prerelease::EMPTY;
versions[0].pre = Prerelease::EMPTY;
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]))
}
pub fn supports_feature(&self, feature: &Feature) -> bool {
self.features.iter().any(|f| feature.is_compatible_with(f))
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub enum Protocol {
#[serde(rename = "nu-plugin")]
#[default]
NuPlugin,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "name")]
pub enum Feature {
LocalSocket,
#[serde(other, skip_serializing)]
Unknown,
}
impl Feature {
pub fn is_compatible_with(&self, other: &Feature) -> bool {
matches!((self, other), (Feature::LocalSocket, Feature::LocalSocket))
}
}
pub fn default_features() -> Vec<Feature> {
vec![
#[cfg(feature = "local-socket")]
Feature::LocalSocket,
]
}