project_dirs/
dir_utils.rs

1use std::path::{Path, PathBuf};
2
3use crate::{FullProjectDirs, ProjectDirs};
4
5/// Filter project directories
6pub trait Filter {
7    /// Return only existing directories.
8    fn filter_existing_dirs(&self) -> ProjectDirs;
9
10    /// Return only non-directory entries in places where directories are expected.
11    /// E.g. `log` file in the place of the `log` directory.
12    fn filter_non_dirs(&self) -> ProjectDirs;
13
14    /// Return only non-existing fs entries.
15    /// E.g. missing `log` directory.
16    fn filter_absent(&self) -> ProjectDirs;
17
18    /// Return only access denied fs entries.
19    /// E.g. no read and execute access to `log` directory.
20    fn filter_denied(&self) -> ProjectDirs;
21
22    /// Basically filter using `!path.is_dir()`, negation of the [`Filter::filter_existing_dirs`]
23    fn filter_non_valid(&self) -> ProjectDirs;
24}
25
26impl Filter for ProjectDirs {
27    fn filter_existing_dirs(&self) -> ProjectDirs {
28        ProjectDirs::new(
29            self.0
30                .iter()
31                .filter(|(_, p)| p.is_dir())
32                .map(|x| (*x.0, x.1.clone()))
33                .collect(),
34        )
35    }
36
37    fn filter_non_dirs(&self) -> ProjectDirs {
38        ProjectDirs::new(
39            self.0
40                .iter()
41                .filter(|(_, p)| p.exists() && !p.is_dir())
42                .map(|x| (*x.0, x.1.clone()))
43                .collect(),
44        )
45    }
46
47    fn filter_absent(&self) -> ProjectDirs {
48        ProjectDirs::new(
49            self.0
50                .iter()
51                .filter(|(_, p)| p.try_exists().map(|e| !e).unwrap_or(false))
52                .map(|x| (*x.0, x.1.clone()))
53                .collect(),
54        )
55    }
56
57    fn filter_denied(&self) -> ProjectDirs {
58        ProjectDirs::new(
59            self.0
60                .iter()
61                .filter(|(_, p)| p.try_exists().is_err())
62                .map(|x| (*x.0, x.1.clone()))
63                .collect(),
64        )
65    }
66
67    fn filter_non_valid(&self) -> ProjectDirs {
68        ProjectDirs::new(
69            self.0
70                .iter()
71                .filter(|(_, p)| !p.is_dir())
72                .map(|x| (*x.0, x.1.clone()))
73                .collect(),
74        )
75    }
76}
77
78/// Mount directories inside the provided mountpoint
79pub trait Mounted {
80    fn mounted(self, mountpoint: &Path) -> Self;
81}
82
83fn strip_root_prefix(p: PathBuf) -> PathBuf {
84    if p.starts_with("/") {
85        p.strip_prefix("/")
86            .expect("Failed to strip / prefix from paths. This is a bug")
87            .to_path_buf()
88    } else {
89        p
90    }
91}
92
93impl Mounted for PathBuf {
94    fn mounted(self, mountpoint: &Path) -> Self {
95        mountpoint.join(strip_root_prefix(self.to_path_buf()))
96    }
97}
98
99impl Mounted for ProjectDirs {
100    fn mounted(self, mountpoint: &Path) -> Self {
101        ProjectDirs::new(
102            self.0
103                .into_iter()
104                .map(|(d, p)| (d, mountpoint.join(strip_root_prefix(p))))
105                .collect(),
106        )
107    }
108}
109
110impl Mounted for FullProjectDirs {
111    fn mounted(self, mountpoint: &Path) -> Self {
112        let pd: ProjectDirs = self.into();
113        pd.mounted(mountpoint).try_into().unwrap()
114    }
115}
116
117/// Retrive home directory
118pub fn home_dir() -> Option<PathBuf> {
119    #[cfg(feature = "nonstd_home_dir")]
120    return home::home_dir();
121
122    #[cfg(not(feature = "nonstd_home_dir"))]
123    return std::env::home_dir();
124}