mirror of
https://github.com/nushell/nushell.git
synced 2025-07-14 05:15:23 +02:00
# 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`
109 lines
3.0 KiB
Rust
109 lines
3.0 KiB
Rust
use std::io::ErrorKind;
|
|
|
|
use nu_plugin_protocol::{PluginInput, PluginOutput};
|
|
use nu_protocol::ShellError;
|
|
use serde::Deserialize;
|
|
|
|
use crate::{Encoder, PluginEncoder};
|
|
|
|
/// A `PluginEncoder` that enables the plugin to communicate with Nushell with MsgPack
|
|
/// serialized data.
|
|
///
|
|
/// Each message is written as a MessagePack object. There is no message envelope or separator.
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct MsgPackSerializer;
|
|
|
|
impl PluginEncoder for MsgPackSerializer {
|
|
fn name(&self) -> &str {
|
|
"msgpack"
|
|
}
|
|
}
|
|
|
|
impl Encoder<PluginInput> for MsgPackSerializer {
|
|
fn encode(
|
|
&self,
|
|
plugin_input: &PluginInput,
|
|
writer: &mut impl std::io::Write,
|
|
) -> Result<(), nu_protocol::ShellError> {
|
|
rmp_serde::encode::write_named(writer, plugin_input).map_err(rmp_encode_err)
|
|
}
|
|
|
|
fn decode(
|
|
&self,
|
|
reader: &mut impl std::io::BufRead,
|
|
) -> Result<Option<PluginInput>, ShellError> {
|
|
let mut de = rmp_serde::Deserializer::new(reader);
|
|
PluginInput::deserialize(&mut de)
|
|
.map(Some)
|
|
.or_else(rmp_decode_err)
|
|
}
|
|
}
|
|
|
|
impl Encoder<PluginOutput> for MsgPackSerializer {
|
|
fn encode(
|
|
&self,
|
|
plugin_output: &PluginOutput,
|
|
writer: &mut impl std::io::Write,
|
|
) -> Result<(), ShellError> {
|
|
rmp_serde::encode::write_named(writer, plugin_output).map_err(rmp_encode_err)
|
|
}
|
|
|
|
fn decode(
|
|
&self,
|
|
reader: &mut impl std::io::BufRead,
|
|
) -> Result<Option<PluginOutput>, ShellError> {
|
|
let mut de = rmp_serde::Deserializer::new(reader);
|
|
PluginOutput::deserialize(&mut de)
|
|
.map(Some)
|
|
.or_else(rmp_decode_err)
|
|
}
|
|
}
|
|
|
|
/// Handle a msgpack encode error
|
|
fn rmp_encode_err(err: rmp_serde::encode::Error) -> ShellError {
|
|
match err {
|
|
rmp_serde::encode::Error::InvalidValueWrite(_) => {
|
|
// I/O error
|
|
ShellError::IOError {
|
|
msg: err.to_string(),
|
|
}
|
|
}
|
|
_ => {
|
|
// Something else
|
|
ShellError::PluginFailedToEncode {
|
|
msg: err.to_string(),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Handle a msgpack decode error. Returns `Ok(None)` on eof
|
|
fn rmp_decode_err<T>(err: rmp_serde::decode::Error) -> Result<Option<T>, ShellError> {
|
|
match err {
|
|
rmp_serde::decode::Error::InvalidMarkerRead(err)
|
|
| rmp_serde::decode::Error::InvalidDataRead(err) => {
|
|
if matches!(err.kind(), ErrorKind::UnexpectedEof) {
|
|
// EOF
|
|
Ok(None)
|
|
} else {
|
|
// I/O error
|
|
Err(ShellError::IOError {
|
|
msg: err.to_string(),
|
|
})
|
|
}
|
|
}
|
|
_ => {
|
|
// Something else
|
|
Err(ShellError::PluginFailedToDecode {
|
|
msg: err.to_string(),
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
crate::serializers::tests::generate_tests!(MsgPackSerializer {});
|
|
}
|