Skip to main content

nu_command/strings/base/
base64.rs

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