shell_rs/
mkdir.rs

1// Copyright (c) 2021 Xu Shaohua <shaohua@biofan.org>. All rights reserved.
2// Use of this source is governed by Apache-2.0 License that can be found
3// in the LICENSE file.
4
5use std::path::Path;
6
7use crate::core::env::expand_env;
8use crate::error::Error;
9
10#[derive(Debug)]
11pub struct Options {
12    /// No error if existing, make parent directories as needed.
13    /// Just like `mkdir -p` command.
14    pub parents: bool,
15
16    /// Set file mode (as in chmod), not a=rwx - umask.
17    pub mode: nc::mode_t,
18
19    /// No error if existing.
20    pub exist_ok: bool,
21
22    pub expand_env: bool,
23}
24
25impl Options {
26    pub fn new() -> Options {
27        Self::default()
28    }
29}
30
31impl Default for Options {
32    fn default() -> Self {
33        Options {
34            parents: false,
35            mode: 0o755,
36            exist_ok: false,
37            expand_env: true,
38        }
39    }
40}
41
42impl Options {
43    pub fn with_parents() -> Self {
44        Self {
45            parents: true,
46            ..Self::default()
47        }
48    }
49}
50
51/// Make directories like `mkdir` command.
52pub fn mkdir<T: AsRef<str>>(directory: T, options: &Options) -> Result<(), Error> {
53    let exist_ok = if options.parents {
54        true
55    } else {
56        options.parents
57    };
58
59    let directory = if options.expand_env {
60        expand_env(directory)
61    } else {
62        directory.as_ref().to_string()
63    };
64
65    let path = Path::new(&directory);
66    do_mkdir(path, options.parents, options.mode, exist_ok).map_err(Into::into)
67}
68
69fn do_mkdir(p: &Path, recursive: bool, mode: nc::mode_t, exist_ok: bool) -> Result<(), nc::Errno> {
70    if p.exists() {
71        if exist_ok {
72            return Ok(());
73        } else {
74            return Err(nc::EEXIST);
75        }
76    }
77    if recursive {
78        match p.parent() {
79            Some(parent) => do_mkdir(parent, recursive, mode, true)?,
80            None => return Err(nc::ENOENT),
81        }
82    }
83
84    unsafe { nc::mkdir(p, mode) }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn test_mkdir() {
93        let mut options = Options::new();
94        options.parents = true;
95        let ret = mkdir("/tmp/test1/test2", &options);
96        assert!(ret.is_ok());
97    }
98}