nu_command/network/url/
encode.rs1use nu_cmd_base::input_handler::{CmdArgument, operate};
2use nu_engine::command_prelude::*;
3
4use percent_encoding::{AsciiSet, NON_ALPHANUMERIC, percent_encode, utf8_percent_encode};
5
6struct Arguments {
7 cell_paths: Option<Vec<CellPath>>,
8 ascii_set: &'static AsciiSet,
9}
10
11static ASCII_SET_ALL: &AsciiSet = NON_ALPHANUMERIC;
12static ASCII_SET_NOT_ALL: &AsciiSet = &NON_ALPHANUMERIC.remove(b'/').remove(b':').remove(b'.');
13
14impl CmdArgument for Arguments {
15 fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
16 self.cell_paths.take()
17 }
18}
19
20#[derive(Clone)]
21pub struct UrlEncode;
22
23impl Command for UrlEncode {
24 fn name(&self) -> &str {
25 "url encode"
26 }
27
28 fn signature(&self) -> Signature {
29 Signature::build("url encode")
30 .input_output_types(vec![
31 (Type::String, Type::String),
32 (Type::Binary, Type::String),
33 (Type::List(Box::new(Type::one_of([Type::String, Type::Binary]))), Type::List(Box::new(Type::String))),
34 (Type::table(), Type::table()),
35 (Type::record(), Type::record()),
36 ])
37 .allow_variants_without_examples(true)
38 .switch(
39 "all",
40 "Encode all non-alphanumeric chars including `/`, `.`, `:`.",
41 Some('a'))
42 .rest(
43 "rest",
44 SyntaxShape::CellPath,
45 "For a data structure input, check strings at the given cell paths, and replace with result.",
46 )
47 .category(Category::Strings)
48 }
49
50 fn description(&self) -> &str {
51 "Converts a string to a percent encoded web safe string."
52 }
53
54 fn search_terms(&self) -> Vec<&str> {
55 vec!["string", "text", "convert"]
56 }
57
58 fn run(
59 &self,
60 engine_state: &EngineState,
61 stack: &mut Stack,
62 call: &Call,
63 input: PipelineData,
64 ) -> Result<PipelineData, ShellError> {
65 let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
66 let cell_paths = Some(cell_paths).filter(|v| !v.is_empty());
67 let ascii_set = match call.has_flag(engine_state, stack, "all")? {
68 true => ASCII_SET_ALL,
69 false => ASCII_SET_NOT_ALL,
70 };
71 let args = Arguments {
72 cell_paths,
73 ascii_set,
74 };
75 operate(action, args, input, call.head, engine_state.signals())
76 }
77
78 fn examples(&self) -> Vec<Example<'_>> {
79 vec![
80 Example {
81 description: "Encode a URL with escape characters.",
82 example: "'https://example.com/foo bar' | url encode",
83 result: Some(Value::test_string("https://example.com/foo%20bar")),
84 },
85 Example {
86 description: "Encode multiple URLs with escape characters in list.",
87 example: "['https://example.com/foo bar' 'https://example.com/a>b' '中文字/eng/12 34'] | url encode",
88 result: Some(Value::list(
89 vec![
90 Value::test_string("https://example.com/foo%20bar"),
91 Value::test_string("https://example.com/a%3Eb"),
92 Value::test_string("%E4%B8%AD%E6%96%87%E5%AD%97/eng/12%2034"),
93 ],
94 Span::test_data(),
95 )),
96 },
97 Example {
98 description: "Encode all non alphanumeric chars with all flag.",
99 example: "'https://example.com/foo bar' | url encode --all",
100 result: Some(Value::test_string(
101 "https%3A%2F%2Fexample%2Ecom%2Ffoo%20bar",
102 )),
103 },
104 Example {
105 description: "Encode a iso-8859-1 encoded string.",
106 example: "'£ rates' | encode iso-8859-1 | url encode",
107 result: Some(Value::test_string("%A3%20rates")),
108 },
109 ]
110 }
111}
112
113fn action(input: &Value, args: &Arguments, head: Span) -> Value {
114 match input {
115 Value::String { val, .. } => {
116 let utf8_percent_encode = utf8_percent_encode(val, args.ascii_set);
117 Value::string(utf8_percent_encode.to_string(), head)
118 }
119 Value::Binary { val, .. } => {
120 let utf8_percent_encode = percent_encode(val, args.ascii_set);
121 Value::string(utf8_percent_encode.to_string(), head)
122 }
123 Value::Error { .. } => input.clone(),
124 _ => Value::error(
125 ShellError::OnlySupportsThisInputType {
126 exp_input_type: "string".into(),
127 wrong_type: input.get_type().to_string(),
128 dst_span: head,
129 src_span: input.span(),
130 },
131 head,
132 ),
133 }
134}
135
136#[cfg(test)]
137mod test {
138 use super::*;
139
140 #[test]
141 fn test_examples() -> nu_test_support::Result {
142 nu_test_support::test().examples(UrlEncode)
143 }
144}