nu_plugin_core/serializers/
msgpack.rs

1use std::io::ErrorKind;
2
3use nu_plugin_protocol::{PluginInput, PluginOutput};
4use nu_protocol::{
5    ShellError,
6    shell_error::{self, io::IoError},
7};
8use serde::Deserialize;
9
10use crate::{Encoder, PluginEncoder};
11
12/// A `PluginEncoder` that enables the plugin to communicate with Nushell with MsgPack
13/// serialized data.
14///
15/// Each message is written as a MessagePack object. There is no message envelope or separator.
16#[derive(Clone, Copy, Debug)]
17pub struct MsgPackSerializer;
18
19impl PluginEncoder for MsgPackSerializer {
20    fn name(&self) -> &str {
21        "msgpack"
22    }
23}
24
25impl Encoder<PluginInput> for MsgPackSerializer {
26    fn encode(
27        &self,
28        plugin_input: &PluginInput,
29        writer: &mut impl std::io::Write,
30    ) -> Result<(), nu_protocol::ShellError> {
31        rmp_serde::encode::write_named(writer, plugin_input).map_err(rmp_encode_err)
32    }
33
34    fn decode(
35        &self,
36        reader: &mut impl std::io::BufRead,
37    ) -> Result<Option<PluginInput>, ShellError> {
38        let mut de = rmp_serde::Deserializer::new(reader);
39        PluginInput::deserialize(&mut de)
40            .map(Some)
41            .or_else(rmp_decode_err)
42    }
43}
44
45impl Encoder<PluginOutput> for MsgPackSerializer {
46    fn encode(
47        &self,
48        plugin_output: &PluginOutput,
49        writer: &mut impl std::io::Write,
50    ) -> Result<(), ShellError> {
51        rmp_serde::encode::write_named(writer, plugin_output).map_err(rmp_encode_err)
52    }
53
54    fn decode(
55        &self,
56        reader: &mut impl std::io::BufRead,
57    ) -> Result<Option<PluginOutput>, ShellError> {
58        let mut de = rmp_serde::Deserializer::new(reader);
59        PluginOutput::deserialize(&mut de)
60            .map(Some)
61            .or_else(rmp_decode_err)
62    }
63}
64
65/// Handle a msgpack encode error
66fn rmp_encode_err(err: rmp_serde::encode::Error) -> ShellError {
67    match err {
68        rmp_serde::encode::Error::InvalidValueWrite(_) => {
69            // I/O error
70            ShellError::Io(IoError::new_internal(
71                // TODO: get a better kind here
72                shell_error::io::ErrorKind::from_std(std::io::ErrorKind::Other),
73                "Could not encode with rmp",
74                nu_protocol::location!(),
75            ))
76        }
77        _ => {
78            // Something else
79            ShellError::PluginFailedToEncode {
80                msg: err.to_string(),
81            }
82        }
83    }
84}
85
86/// Handle a msgpack decode error. Returns `Ok(None)` on eof
87fn rmp_decode_err<T>(err: rmp_serde::decode::Error) -> Result<Option<T>, ShellError> {
88    match err {
89        rmp_serde::decode::Error::InvalidMarkerRead(err)
90        | rmp_serde::decode::Error::InvalidDataRead(err) => {
91            if matches!(err.kind(), ErrorKind::UnexpectedEof) {
92                // EOF
93                Ok(None)
94            } else {
95                // I/O error
96                Err(ShellError::Io(IoError::new_internal(
97                    // TODO: get a better kind here
98                    shell_error::io::ErrorKind::from_std(std::io::ErrorKind::Other),
99                    "Could not decode with rmp",
100                    nu_protocol::location!(),
101                )))
102            }
103        }
104        _ => {
105            // Something else
106            Err(ShellError::PluginFailedToDecode {
107                msg: err.to_string(),
108            })
109        }
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116    crate::serializers::tests::generate_tests!(MsgPackSerializer {});
117}