Skip to main content

nu_command/filesystem/
umkdir.rs

1use nu_engine::command_prelude::*;
2use nu_protocol::{NuGlob, shell_error::generic::GenericError};
3use uu_mkdir::mkdir;
4use uucore::{localized_help_template, translate};
5
6#[derive(Clone)]
7pub struct UMkdir;
8
9const IS_RECURSIVE: bool = true;
10const DEFAULT_MODE: u32 = 0o777;
11
12#[cfg(target_family = "unix")]
13fn get_mode() -> u32 {
14    !nu_system::get_umask() & DEFAULT_MODE
15}
16
17#[cfg(not(target_family = "unix"))]
18fn get_mode() -> u32 {
19    DEFAULT_MODE
20}
21
22impl Command for UMkdir {
23    fn name(&self) -> &str {
24        "mkdir"
25    }
26
27    fn description(&self) -> &str {
28        "Create directories, with intermediary directories if required using uutils/coreutils mkdir."
29    }
30
31    fn search_terms(&self) -> Vec<&str> {
32        vec![
33            "directory",
34            "folder",
35            "create",
36            "make_dirs",
37            "coreutils",
38            "md",
39        ]
40    }
41
42    fn signature(&self) -> Signature {
43        Signature::build("mkdir")
44            .input_output_types(vec![
45                (Type::Nothing, Type::Nothing),
46                (
47                    Type::Nothing,
48                    Type::Table(
49                        [
50                            ("path".to_string(), Type::String),
51                            ("created".to_string(), Type::Bool),
52                            (
53                                "error".to_string(),
54                                Type::OneOf([Type::Nothing, Type::String].into()),
55                            ),
56                        ]
57                        .into(),
58                    ),
59                ),
60            ])
61            .rest(
62                "rest",
63                SyntaxShape::OneOf(vec![SyntaxShape::GlobPattern, SyntaxShape::Directory]),
64                "The name(s) of the path(s) to create.",
65            )
66            .switch(
67                "verbose",
68                "Print a message for each created directory.",
69                Some('v'),
70            )
71            .category(Category::FileSystem)
72    }
73
74    fn run(
75        &self,
76        engine_state: &EngineState,
77        stack: &mut Stack,
78        call: &Call,
79        _input: PipelineData,
80    ) -> Result<PipelineData, ShellError> {
81        // setup the uutils error translation
82        let _ = localized_help_template("mkdir");
83
84        let cwd = engine_state.cwd(Some(stack))?.into_std_path_buf();
85        let mut directories = call
86            .rest::<Spanned<NuGlob>>(engine_state, stack, 0)?
87            .into_iter()
88            .map(|dir| {
89                (
90                    nu_path::expand_path_with(dir.item.as_ref(), &cwd, dir.item.is_expand()),
91                    dir.span,
92                )
93            })
94            .peekable();
95
96        let is_verbose = call.has_flag(engine_state, stack, "verbose")?;
97
98        if directories.peek().is_none() {
99            return Err(ShellError::MissingParameter {
100                param_name: "requires directory paths".to_string(),
101                span: call.head,
102            });
103        }
104
105        let config = uu_mkdir::Config {
106            recursive: IS_RECURSIVE,
107            mode: get_mode(),
108            verbose: false,
109            set_security_context: false,
110            context: None,
111        };
112
113        let mut verbose_out = Vec::new();
114        let mut err = None;
115        for (dir, dir_span) in directories {
116            if let Err(error) = mkdir(&dir, &config) {
117                let shell_error = ShellError::Generic(GenericError::new(
118                    format!("{error}"),
119                    translate!(&error.to_string()),
120                    dir_span,
121                ));
122
123                if is_verbose {
124                    verbose_out.push(
125                        record! {
126                            "path" => Value::string(dir.display().to_string(), call.head),
127                            "created" => Value::bool(false, call.head),
128                            "error" => Value::string(format!("{error}"), call.head),
129                        }
130                        .into_value(call.head),
131                    )
132                } else {
133                    err = Some(shell_error);
134                }
135            } else if is_verbose {
136                verbose_out.push(
137                    record! {
138                        "path" => Value::string(dir.display().to_string(), call.head),
139                        "created" => Value::bool(true, call.head),
140                        "error" => Value::nothing(call.head),
141                    }
142                    .into_value(call.head),
143                );
144            }
145        }
146
147        if is_verbose {
148            Ok(PipelineData::value(
149                Value::list(verbose_out, call.head),
150                None,
151            ))
152        } else if let Some(err) = err {
153            Err(err)
154        } else {
155            Ok(PipelineData::empty())
156        }
157    }
158
159    fn examples(&self) -> Vec<Example<'_>> {
160        vec![
161            Example {
162                description: "Make a directory named foo.",
163                example: "mkdir foo",
164                result: None,
165            },
166            Example {
167                description: "Make multiple directories and show the paths created.",
168                example: "mkdir -v foo/bar foo2",
169                result: Some(Value::test_list(vec![
170                    Value::record(
171                        record! {
172                            "path" => Value::string("foo/bar".to_string(), Span::test_data()),
173                            "created" => Value::bool(true, Span::test_data()),
174                            "error" => Value::nothing(Span::test_data()),
175                        },
176                        Span::test_data(),
177                    ),
178                    Value::record(
179                        record! {
180                            "path" => Value::string("foo2".to_string(), Span::test_data()),
181                            "created" => Value::bool(true, Span::test_data()),
182                            "error" => Value::nothing(Span::test_data()),
183                        },
184                        Span::test_data(),
185                    ),
186                ])),
187            },
188        ]
189    }
190}