nu_command/strings/base/
base32.rs

1use data_encoding::Encoding;
2
3use nu_engine::command_prelude::*;
4
5const EXTRA_USAGE: &str = r"The default alphabet is taken from RFC 4648, section 6.
6
7Note this command will collect stream input.";
8
9#[derive(Clone)]
10pub struct DecodeBase32;
11
12impl Command for DecodeBase32 {
13    fn name(&self) -> &str {
14        "decode base32"
15    }
16
17    fn signature(&self) -> Signature {
18        Signature::build("decode base32")
19            .input_output_types(vec![(Type::String, Type::Binary)])
20            .allow_variants_without_examples(true)
21            .switch("nopad", "Do not pad the output.", None)
22            .category(Category::Formats)
23    }
24
25    fn description(&self) -> &str {
26        "Decode a Base32 value."
27    }
28
29    fn extra_description(&self) -> &str {
30        EXTRA_USAGE
31    }
32
33    fn examples(&self) -> Vec<Example> {
34        vec![
35            Example {
36                description: "Decode arbitrary binary data",
37                example: r#""AEBAGBAF" | decode base32"#,
38                result: Some(Value::test_binary(vec![1, 2, 3, 4, 5])),
39            },
40            Example {
41                description: "Decode an encoded string",
42                example: r#""NBUQ====" | decode base32 | decode"#,
43                result: None,
44            },
45            Example {
46                description: "Parse a string without padding",
47                example: r#""NBUQ" | decode base32 --nopad"#,
48                result: Some(Value::test_binary(vec![0x68, 0x69])),
49            },
50        ]
51    }
52
53    fn is_const(&self) -> bool {
54        true
55    }
56
57    fn run(
58        &self,
59        engine_state: &EngineState,
60        stack: &mut Stack,
61        call: &Call,
62        input: PipelineData,
63    ) -> Result<PipelineData, ShellError> {
64        let encoding = if call.has_flag(engine_state, stack, "nopad")? {
65            data_encoding::BASE32_NOPAD
66        } else {
67            data_encoding::BASE32
68        };
69        super::decode(encoding, call.span(), input)
70    }
71
72    fn run_const(
73        &self,
74        working_set: &StateWorkingSet,
75        call: &Call,
76        input: PipelineData,
77    ) -> Result<PipelineData, ShellError> {
78        let encoding = if call.has_flag_const(working_set, "nopad")? {
79            data_encoding::BASE32_NOPAD
80        } else {
81            data_encoding::BASE32
82        };
83        super::decode(encoding, call.span(), input)
84    }
85}
86
87#[derive(Clone)]
88pub struct EncodeBase32;
89
90impl Command for EncodeBase32 {
91    fn name(&self) -> &str {
92        "encode base32"
93    }
94
95    fn signature(&self) -> Signature {
96        Signature::build("encode base32")
97            .input_output_types(vec![
98                (Type::String, Type::String),
99                (Type::Binary, Type::String),
100            ])
101            .switch("nopad", "Don't accept padding.", None)
102            .category(Category::Formats)
103    }
104
105    fn description(&self) -> &str {
106        "Encode a string or binary value using Base32."
107    }
108
109    fn extra_description(&self) -> &str {
110        EXTRA_USAGE
111    }
112
113    fn examples(&self) -> Vec<Example> {
114        vec![
115            Example {
116                description: "Encode a binary value",
117                example: r#"0x[01 02 10] | encode base32"#,
118                result: Some(Value::test_string("AEBBA===")),
119            },
120            Example {
121                description: "Encode a string",
122                example: r#""hello there" | encode base32"#,
123                result: Some(Value::test_string("NBSWY3DPEB2GQZLSMU======")),
124            },
125            Example {
126                description: "Don't apply padding to the output",
127                example: r#""hi" | encode base32 --nopad"#,
128                result: Some(Value::test_string("NBUQ")),
129            },
130        ]
131    }
132
133    fn is_const(&self) -> bool {
134        true
135    }
136
137    fn run(
138        &self,
139        engine_state: &EngineState,
140        stack: &mut Stack,
141        call: &Call,
142        input: PipelineData,
143    ) -> Result<PipelineData, ShellError> {
144        let encoding = if call.has_flag(engine_state, stack, "nopad")? {
145            data_encoding::BASE32_NOPAD
146        } else {
147            data_encoding::BASE32
148        };
149        super::encode(encoding, call.span(), input)
150    }
151
152    fn run_const(
153        &self,
154        working_set: &StateWorkingSet,
155        call: &Call,
156        input: PipelineData,
157    ) -> Result<PipelineData, ShellError> {
158        let encoding = if call.has_flag_const(working_set, "nopad")? {
159            data_encoding::BASE32_NOPAD
160        } else {
161            data_encoding::BASE32
162        };
163        super::encode(encoding, call.span(), input)
164    }
165}
166
167#[cfg(test)]
168mod tests {
169    use super::*;
170
171    #[test]
172    fn test_examples_decode() {
173        crate::test_examples(DecodeBase32)
174    }
175
176    #[test]
177    fn test_examples_encode() {
178        crate::test_examples(EncodeBase32)
179    }
180}