mkdirp/
lib.rs

1#![deny(warnings, missing_docs)]
2
3//! mkdir -p
4
5use std::fs;
6use std::io;
7use std::path::{Path, PathBuf};
8
9/// Create a new directory, ignore if it already exists.
10///
11/// Returns the first created directory if some of the
12/// paths already existed, `None` if no new directories
13/// were created.
14pub fn mkdirp<P: AsRef<Path>>(path: P) -> io::Result<Option<PathBuf>> {
15  let path = path.as_ref();
16  if path == Path::new("") {
17    return Ok(None);
18  }
19
20  match fs::create_dir(path) {
21    Ok(()) => return Ok(Some(path.to_owned())),
22    Err(ref e) if e.kind() == io::ErrorKind::NotFound => {}
23    Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => return Ok(None),
24    Err(_) if path.is_dir() => return Ok(None),
25    Err(e) => return Err(e),
26  }
27  let created = match path.parent() {
28    Some(p) => mkdirp(p),
29    None => {
30      return Err(io::Error::new(
31        io::ErrorKind::Other,
32        "failed to create whole tree",
33      ))
34    }
35  };
36  match fs::create_dir(path) {
37    Ok(()) => created,
38    Err(_) if path.is_dir() => created,
39    Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => created,
40    Err(e) => Err(e),
41  }
42}
43
44#[cfg(test)]
45mod tests {
46  use super::*;
47  #[test]
48  fn return_dir() {
49    let mut dir = std::env::temp_dir();
50    dir.push("mkdirp-test");
51    let test_path = dir.clone();
52    fs::remove_dir_all(&test_path).ok();
53    fs::create_dir(&test_path).unwrap();
54    dir.push("nested-1");
55    let expected = dir.clone();
56    dir.push("nested-2");
57    dir.push("nested-3");
58    let ret = mkdirp(dir.as_path()).unwrap().unwrap();
59    fs::remove_dir_all(test_path).unwrap();
60    assert_eq!(ret, expected);
61  }
62
63  #[test]
64  fn noop() {
65    let dir = std::env::temp_dir();
66    let ret = mkdirp(dir.as_path()).unwrap();
67    assert_eq!(ret, None);
68  }
69}