nu_command/strings/base/
base64.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 4.  A URL-safe version is available.
6
7Note this command will collect stream input.";
8
9fn get_encoding_from_flags(url: bool, nopad: bool) -> Encoding {
10    match (url, nopad) {
11        (false, false) => data_encoding::BASE64,
12        (false, true) => data_encoding::BASE64_NOPAD,
13        (true, false) => data_encoding::BASE64URL,
14        (true, true) => data_encoding::BASE64URL_NOPAD,
15    }
16}
17
18fn get_encoding(
19    engine_state: &EngineState,
20    stack: &mut Stack,
21    call: &Call,
22) -> Result<Encoding, ShellError> {
23    let url = call.has_flag(engine_state, stack, "url")?;
24    let nopad = call.has_flag(engine_state, stack, "nopad")?;
25
26    Ok(get_encoding_from_flags(url, nopad))
27}
28
29fn get_encoding_const(working_set: &StateWorkingSet, call: &Call) -> Result<Encoding, ShellError> {
30    let url = call.has_flag_const(working_set, "url")?;
31    let nopad = call.has_flag_const(working_set, "nopad")?;
32
33    Ok(get_encoding_from_flags(url, nopad))
34}
35
36#[derive(Clone)]
37pub struct DecodeBase64;
38
39impl Command for DecodeBase64 {
40    fn name(&self) -> &str {
41        "decode base64"
42    }
43
44    fn signature(&self) -> Signature {
45        Signature::build("decode base64")
46            .input_output_types(vec![(Type::String, Type::Binary)])
47            .allow_variants_without_examples(true)
48            .switch("url", "Decode the URL-safe Base64 version.", None)
49            .switch("nopad", "Reject padding.", None)
50            .category(Category::Formats)
51    }
52
53    fn description(&self) -> &str {
54        "Decode a Base64 value."
55    }
56
57    fn extra_description(&self) -> &str {
58        EXTRA_USAGE
59    }
60
61    fn examples(&self) -> Vec<Example> {
62        vec![
63            Example {
64                description: "Decode a Base64 string",
65                example: r#""U29tZSBEYXRh" | decode base64 | decode"#,
66                result: None,
67            },
68            Example {
69                description: "Decode arbitrary data",
70                example: r#""/w==" | decode base64"#,
71                result: Some(Value::test_binary(vec![0xFF])),
72            },
73            Example {
74                description: "Decode a URL-safe Base64 string",
75                example: r#""_w==" | decode base64 --url"#,
76                result: Some(Value::test_binary(vec![0xFF])),
77            },
78        ]
79    }
80
81    fn is_const(&self) -> bool {
82        true
83    }
84
85    fn run(
86        &self,
87        engine_state: &EngineState,
88        stack: &mut Stack,
89        call: &Call,
90        input: PipelineData,
91    ) -> Result<PipelineData, ShellError> {
92        let encoding = get_encoding(engine_state, stack, call)?;
93        super::decode(encoding, call.head, input)
94    }
95
96    fn run_const(
97        &self,
98        working_set: &StateWorkingSet,
99        call: &Call,
100        input: PipelineData,
101    ) -> Result<PipelineData, ShellError> {
102        let encoding = get_encoding_const(working_set, call)?;
103        super::decode(encoding, call.head, input)
104    }
105}
106
107#[derive(Clone)]
108pub struct EncodeBase64;
109
110impl Command for EncodeBase64 {
111    fn name(&self) -> &str {
112        "encode base64"
113    }
114
115    fn signature(&self) -> Signature {
116        Signature::build("encode base64")
117            .input_output_types(vec![
118                (Type::String, Type::String),
119                (Type::Binary, Type::String),
120            ])
121            .switch("url", "Use the URL-safe Base64 version.", None)
122            .switch("nopad", "Don't pad the output.", None)
123            .category(Category::Formats)
124    }
125
126    fn description(&self) -> &str {
127        "Encode a string or binary value using Base64."
128    }
129
130    fn extra_description(&self) -> &str {
131        EXTRA_USAGE
132    }
133
134    fn examples(&self) -> Vec<Example> {
135        vec![
136            Example {
137                description: "Encode a string with Base64",
138                example: r#""Alphabet from A to Z" | encode base64"#,
139                result: Some(Value::test_string("QWxwaGFiZXQgZnJvbSBBIHRvIFo=")),
140            },
141            Example {
142                description: "Encode arbitrary data",
143                example: r#"0x[BE EE FF] | encode base64"#,
144                result: Some(Value::test_string("vu7/")),
145            },
146            Example {
147                description: "Use a URL-safe alphabet",
148                example: r#"0x[BE EE FF] | encode base64 --url"#,
149                result: Some(Value::test_string("vu7_")),
150            },
151        ]
152    }
153
154    fn is_const(&self) -> bool {
155        true
156    }
157
158    fn run(
159        &self,
160        engine_state: &EngineState,
161        stack: &mut Stack,
162        call: &Call,
163        input: PipelineData,
164    ) -> Result<PipelineData, ShellError> {
165        let encoding = get_encoding(engine_state, stack, call)?;
166        super::encode(encoding, call.head, input)
167    }
168
169    fn run_const(
170        &self,
171        working_set: &StateWorkingSet,
172        call: &Call,
173        input: PipelineData,
174    ) -> Result<PipelineData, ShellError> {
175        let encoding = get_encoding_const(working_set, call)?;
176        super::encode(encoding, call.head, input)
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183
184    #[test]
185    fn test_examples_decode() {
186        crate::test_examples(DecodeBase64)
187    }
188
189    #[test]
190    fn test_examples_encode() {
191        crate::test_examples(EncodeBase64)
192    }
193}