nu_command/formats/to/
msgpackz.rs

1use std::io::Write;
2
3use nu_engine::command_prelude::*;
4use nu_protocol::shell_error::io::IoError;
5
6use super::msgpack::write_value;
7
8const BUFFER_SIZE: usize = 65536;
9const DEFAULT_QUALITY: u32 = 3; // 1 can be very bad
10const DEFAULT_WINDOW_SIZE: u32 = 20;
11
12#[derive(Clone)]
13pub struct ToMsgpackz;
14
15impl Command for ToMsgpackz {
16    fn name(&self) -> &str {
17        "to msgpackz"
18    }
19
20    fn signature(&self) -> Signature {
21        Signature::build(self.name())
22            .input_output_type(Type::Any, Type::Binary)
23            .named(
24                "quality",
25                SyntaxShape::Int,
26                "Quality of brotli compression (default 3)",
27                Some('q'),
28            )
29            .named(
30                "window-size",
31                SyntaxShape::Int,
32                "Window size for brotli compression (default 20)",
33                Some('w'),
34            )
35            .switch(
36                "serialize",
37                "serialize nushell types that cannot be deserialized",
38                Some('s'),
39            )
40            .category(Category::Formats)
41    }
42
43    fn description(&self) -> &str {
44        "Convert Nu values into brotli-compressed MessagePack."
45    }
46
47    fn extra_description(&self) -> &str {
48        "This is the format used by the plugin registry file ($nu.plugin-path)."
49    }
50
51    fn run(
52        &self,
53        engine_state: &EngineState,
54        stack: &mut Stack,
55        call: &Call,
56        input: PipelineData,
57    ) -> Result<PipelineData, ShellError> {
58        fn to_u32(n: Spanned<i64>) -> Result<Spanned<u32>, ShellError> {
59            u32::try_from(n.item)
60                .map_err(|err| ShellError::CantConvert {
61                    to_type: "u32".into(),
62                    from_type: "int".into(),
63                    span: n.span,
64                    help: Some(err.to_string()),
65                })
66                .map(|o| o.into_spanned(n.span))
67        }
68
69        let quality = call
70            .get_flag(engine_state, stack, "quality")?
71            .map(to_u32)
72            .transpose()?;
73        let window_size = call
74            .get_flag(engine_state, stack, "window-size")?
75            .map(to_u32)
76            .transpose()?;
77        let serialize_types = call.has_flag(engine_state, stack, "serialize")?;
78
79        let value_span = input.span().unwrap_or(call.head);
80        let value = input.into_value(value_span)?;
81        let mut out_buf = vec![];
82        let mut out = brotli::CompressorWriter::new(
83            &mut out_buf,
84            BUFFER_SIZE,
85            quality.map(|q| q.item).unwrap_or(DEFAULT_QUALITY),
86            window_size.map(|w| w.item).unwrap_or(DEFAULT_WINDOW_SIZE),
87        );
88
89        write_value(
90            &mut out,
91            &value,
92            0,
93            engine_state,
94            call.head,
95            serialize_types,
96        )?;
97        out.flush()
98            .map_err(|err| IoError::new(err, call.head, None))?;
99        drop(out);
100
101        Ok(Value::binary(out_buf, call.head).into_pipeline_data())
102    }
103}