nu_protocol/plugin/registry_file/
mod.rsuse std::{
io::{Read, Write},
path::PathBuf,
};
use serde::{Deserialize, Serialize};
use crate::{PluginIdentity, PluginMetadata, PluginSignature, ShellError, Span};
const BUFFER_SIZE: usize = 65536;
const COMPRESSION_QUALITY: u32 = 3; const WIN_SIZE: u32 = 20; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PluginRegistryFile {
pub nushell_version: String,
pub plugins: Vec<PluginRegistryItem>,
}
impl Default for PluginRegistryFile {
fn default() -> Self {
Self::new()
}
}
impl PluginRegistryFile {
pub fn new() -> PluginRegistryFile {
PluginRegistryFile {
nushell_version: env!("CARGO_PKG_VERSION").to_owned(),
plugins: vec![],
}
}
pub fn read_from(
reader: impl Read,
error_span: Option<Span>,
) -> Result<PluginRegistryFile, ShellError> {
let brotli_reader = brotli::Decompressor::new(reader, BUFFER_SIZE);
rmp_serde::from_read(brotli_reader).map_err(|err| ShellError::GenericError {
error: format!("Failed to load plugin file: {err}"),
msg: "plugin file load attempted here".into(),
span: error_span,
help: Some(
"it may be corrupt. Try deleting it and registering your plugins again".into(),
),
inner: vec![],
})
}
pub fn write_to(
&mut self,
writer: impl Write,
error_span: Option<Span>,
) -> Result<(), ShellError> {
env!("CARGO_PKG_VERSION").clone_into(&mut self.nushell_version);
let mut brotli_writer =
brotli::CompressorWriter::new(writer, BUFFER_SIZE, COMPRESSION_QUALITY, WIN_SIZE);
rmp_serde::encode::write_named(&mut brotli_writer, self)
.map_err(|err| err.to_string())
.and_then(|_| brotli_writer.flush().map_err(|err| err.to_string()))
.map_err(|err| ShellError::GenericError {
error: "Failed to save plugin file".into(),
msg: "plugin file save attempted here".into(),
span: error_span,
help: Some(err.to_string()),
inner: vec![],
})
}
pub fn upsert_plugin(&mut self, item: PluginRegistryItem) {
if let Some(existing_item) = self.plugins.iter_mut().find(|p| p.name == item.name) {
*existing_item = item;
} else {
self.plugins.push(item);
self.plugins
.sort_by(|item1, item2| item1.name.cmp(&item2.name));
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PluginRegistryItem {
pub name: String,
pub filename: PathBuf,
pub shell: Option<PathBuf>,
#[serde(flatten)]
pub data: PluginRegistryItemData,
}
impl PluginRegistryItem {
pub fn new(
identity: &PluginIdentity,
metadata: PluginMetadata,
mut commands: Vec<PluginSignature>,
) -> PluginRegistryItem {
commands.sort_by(|cmd1, cmd2| cmd1.sig.name.cmp(&cmd2.sig.name));
PluginRegistryItem {
name: identity.name().to_owned(),
filename: identity.filename().to_owned(),
shell: identity.shell().map(|p| p.to_owned()),
data: PluginRegistryItemData::Valid { metadata, commands },
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
pub enum PluginRegistryItemData {
Valid {
#[serde(default)]
metadata: PluginMetadata,
commands: Vec<PluginSignature>,
},
#[serde(
serialize_with = "serialize_invalid",
deserialize_with = "deserialize_invalid"
)]
Invalid,
}
fn serialize_invalid<S>(serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
().serialize(serializer)
}
fn deserialize_invalid<'de, D>(deserializer: D) -> Result<(), D::Error>
where
D: serde::Deserializer<'de>,
{
serde::de::IgnoredAny::deserialize(deserializer)?;
Ok(())
}
#[cfg(test)]
mod tests;