nu_command/path/
basename.rs

1use super::PathSubcommandArguments;
2use nu_engine::command_prelude::*;
3use nu_protocol::engine::StateWorkingSet;
4use std::path::Path;
5
6struct Arguments {
7    replace: Option<Spanned<String>>,
8}
9
10impl PathSubcommandArguments for Arguments {}
11
12#[derive(Clone)]
13pub struct PathBasename;
14
15impl Command for PathBasename {
16    fn name(&self) -> &str {
17        "path basename"
18    }
19
20    fn signature(&self) -> Signature {
21        Signature::build("path basename")
22            .input_output_types(vec![
23                (Type::String, Type::String),
24                (
25                    Type::List(Box::new(Type::String)),
26                    Type::List(Box::new(Type::String)),
27                ),
28            ])
29            .named(
30                "replace",
31                SyntaxShape::String,
32                "Return original path with basename replaced by this string",
33                Some('r'),
34            )
35            .category(Category::Path)
36    }
37
38    fn description(&self) -> &str {
39        "Get the final component of a path."
40    }
41
42    fn is_const(&self) -> bool {
43        true
44    }
45
46    fn run(
47        &self,
48        engine_state: &EngineState,
49        stack: &mut Stack,
50        call: &Call,
51        input: PipelineData,
52    ) -> Result<PipelineData, ShellError> {
53        let head = call.head;
54        let args = Arguments {
55            replace: call.get_flag(engine_state, stack, "replace")?,
56        };
57
58        // This doesn't match explicit nulls
59        if matches!(input, PipelineData::Empty) {
60            return Err(ShellError::PipelineEmpty { dst_span: head });
61        }
62        input.map(
63            move |value| super::operate(&get_basename, &args, value, head),
64            engine_state.signals(),
65        )
66    }
67
68    fn run_const(
69        &self,
70        working_set: &StateWorkingSet,
71        call: &Call,
72        input: PipelineData,
73    ) -> Result<PipelineData, ShellError> {
74        let head = call.head;
75        let args = Arguments {
76            replace: call.get_flag_const(working_set, "replace")?,
77        };
78
79        // This doesn't match explicit nulls
80        if matches!(input, PipelineData::Empty) {
81            return Err(ShellError::PipelineEmpty { dst_span: head });
82        }
83        input.map(
84            move |value| super::operate(&get_basename, &args, value, head),
85            working_set.permanent().signals(),
86        )
87    }
88
89    #[cfg(windows)]
90    fn examples(&self) -> Vec<Example> {
91        vec![
92            Example {
93                description: "Get basename of a path",
94                example: "'C:\\Users\\joe\\test.txt' | path basename",
95                result: Some(Value::test_string("test.txt")),
96            },
97            Example {
98                description: "Get basename of a list of paths",
99                example: r"[ C:\Users\joe, C:\Users\doe ] | path basename",
100                result: Some(Value::test_list(vec![
101                    Value::test_string("joe"),
102                    Value::test_string("doe"),
103                ])),
104            },
105            Example {
106                description: "Replace basename of a path",
107                example: "'C:\\Users\\joe\\test.txt' | path basename --replace 'spam.png'",
108                result: Some(Value::test_string("C:\\Users\\joe\\spam.png")),
109            },
110        ]
111    }
112
113    #[cfg(not(windows))]
114    fn examples(&self) -> Vec<Example> {
115        vec![
116            Example {
117                description: "Get basename of a path",
118                example: "'/home/joe/test.txt' | path basename",
119                result: Some(Value::test_string("test.txt")),
120            },
121            Example {
122                description: "Get basename of a list of paths",
123                example: "[ /home/joe, /home/doe ] | path basename",
124                result: Some(Value::test_list(vec![
125                    Value::test_string("joe"),
126                    Value::test_string("doe"),
127                ])),
128            },
129            Example {
130                description: "Replace basename of a path",
131                example: "'/home/joe/test.txt' | path basename --replace 'spam.png'",
132                result: Some(Value::test_string("/home/joe/spam.png")),
133            },
134        ]
135    }
136}
137
138fn get_basename(path: &Path, span: Span, args: &Arguments) -> Value {
139    match &args.replace {
140        Some(r) => Value::string(path.with_file_name(r.item.clone()).to_string_lossy(), span),
141        None => Value::string(
142            match path.file_name() {
143                Some(n) => n.to_string_lossy(),
144                None => "".into(),
145            },
146            span,
147        ),
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154
155    #[test]
156    fn test_examples() {
157        use crate::test_examples;
158
159        test_examples(PathBasename {})
160    }
161}