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