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}