nd_util/
lib.rs

1#[cfg(feature = "download-to-file")]
2mod download_to_file;
3#[cfg(feature = "download-to-file")]
4pub use self::download_to_file::download_to_file;
5
6#[cfg(feature = "drop-remove-path")]
7mod drop_remove_path;
8#[cfg(feature = "drop-remove-path")]
9pub use self::drop_remove_path::DropRemovePath;
10
11#[cfg(feature = "download-to-path")]
12mod download_to_path;
13#[cfg(feature = "download-to-path")]
14pub use self::download_to_path::download_to_path;
15
16#[cfg(feature = "arc-anyhow-error")]
17mod arc_anyhow_error;
18#[cfg(feature = "arc-anyhow-error")]
19pub use self::arc_anyhow_error::ArcAnyhowError;
20
21use std::ffi::OsStr;
22use std::ffi::OsString;
23use std::path::Path;
24use std::path::PathBuf;
25
26/// Push an extension to a [`PathBuf`].
27pub fn push_extension<S>(path: &mut PathBuf, extension: S)
28where
29    S: AsRef<OsStr>,
30{
31    let extension = extension.as_ref();
32
33    // Bail out early if there is no extension, simply setting one.
34    if path.extension().is_none() {
35        path.set_extension(extension);
36        return;
37    }
38
39    // Take the path memory, make it a string, push the extension, and restore the argument path.
40    //
41    // Ideally, I wouldn't take ownership of the original string,
42    // but there is no API to push arbitrary bytes to a [`PathBuf`].
43    // Similarly, there is no api to access the underlying [`OsString`] of a [`PathBuf`].
44    let mut path_string = OsString::from(std::mem::take(path));
45    path_string.reserve(extension.len() + 1);
46    path_string.push(".");
47    path_string.push(extension);
48    std::mem::swap(path, &mut path_string.into());
49}
50
51/// Push an extension to a [`Path`], returning a new [`PathBuf`].
52pub fn with_push_extension<P, S>(path: P, extension: S) -> PathBuf
53where
54    P: AsRef<Path>,
55    S: AsRef<OsStr>,
56{
57    let path = path.as_ref();
58    let extension = extension.as_ref();
59
60    // Bail out early if there is no extension, simply setting one.
61    if path.extension().is_none() {
62        return path.with_extension(extension);
63    }
64
65    // Change the path into an OsString so we can push arbitrary bytes to it,
66    // then change it into a PathBuf so we can return it.
67    let mut path_string = OsString::from(path);
68    path_string.reserve(extension.len() + 1);
69    path_string.push(".");
70    path_string.push(extension);
71    PathBuf::from(path_string)
72}
73
74/// Try to create a dir at the given path.
75///
76/// # Returns
77/// Returns `Ok(true)` if the dir was created.
78/// Returns `Ok(false)` if the dir already exists.
79/// Returns and error if there was an error creating the dir.
80pub fn try_create_dir<P>(path: P) -> std::io::Result<bool>
81where
82    P: AsRef<Path>,
83{
84    match std::fs::create_dir(path) {
85        Ok(()) => Ok(true),
86        Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => Ok(false),
87        Err(error) => Err(error),
88    }
89}
90
91/// Try to remove a dir at the given path.
92///
93/// # Returns
94/// Returns `Ok(true)` if the dir was removed.
95/// Returns `Ok(false)` if the dir did not exist.
96/// Returns and error if there was an error removing the dir.
97pub fn try_remove_dir<P>(path: P) -> std::io::Result<bool>
98where
99    P: AsRef<Path>,
100{
101    match std::fs::remove_dir(path) {
102        Ok(()) => Ok(true),
103        Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(false),
104        Err(error) => Err(error),
105    }
106}
107
108#[cfg(test)]
109mod test {
110    use super::*;
111
112    #[test]
113    fn push_path_extension_works() {
114        let base_path = PathBuf::from("file.txt");
115        let extension = "part";
116        let with_push_extension_path = with_push_extension(&base_path, extension);
117        let mut push_extension_path = base_path;
118        push_extension(&mut push_extension_path, extension);
119
120        let expected = Path::new("file.txt.part");
121        assert!(with_push_extension_path == expected);
122        assert!(push_extension_path == expected);
123
124        let base_path = PathBuf::from("file");
125        let extension = "part";
126        let with_push_extension_path = with_push_extension(&base_path, extension);
127        let mut push_extension_path = base_path;
128        push_extension(&mut push_extension_path, extension);
129
130        let expected = Path::new("file.part");
131        assert!(with_push_extension_path == expected);
132        assert!(push_extension_path == expected);
133    }
134
135    #[test]
136    fn try_create_dir_works() {
137        let path = "test_tmp/try_create_dir";
138
139        std::fs::create_dir_all("test_tmp").expect("failed to create tmp dir");
140
141        try_remove_dir(path).expect("failed to remove dir");
142        assert!(try_create_dir(path).expect("failed to create dir"));
143        assert!(!try_create_dir(path).expect("failed to create dir"));
144        assert!(try_remove_dir(path).expect("failed to remove dir"));
145        assert!(!try_remove_dir(path).expect("failed to remove dir"));
146    }
147}