project_dirs/
proj_dirs.rs

1use crate::Directory;
2use std::collections::HashMap;
3use std::path::PathBuf;
4
5/// List of missing directories for [`ProjectDirs`] to [`FullProjectDirs`] conversion
6pub type MissingError = Vec<Directory>;
7
8/// Project directories by directory type ([`Directory`] to [`PathBuf`] mapping)
9#[derive(Debug, Clone, PartialEq, Eq)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
12pub struct ProjectDirs(pub HashMap<Directory, PathBuf>);
13
14impl ProjectDirs {
15    pub fn get(&self, dir: &Directory) -> Option<&PathBuf> {
16        self.0.get(dir)
17    }
18}
19
20/// Fully defined project directories by directory type ([`Directory`] to [`PathBuf`] mapping)
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct FullProjectDirs {
25    /// Binaries directory. This is where the project executable(s) is/are located
26    pub bin: PathBuf,
27    /// Non-essential data, usually used to speed up the application
28    pub cache: PathBuf,
29    /// General config store. E.g. conf.d dir and other config files
30    pub config: PathBuf,
31    /// Essential files for application like db files, cross-session data etc.
32    pub data: PathBuf,
33    /// Include dir for C/C++ headers.
34    pub include: PathBuf,
35    /// Shared library dir for the app
36    pub lib: PathBuf,
37    /// Directory handling application logs
38    pub log: PathBuf,
39    /// Project root dir. Not meaningful for strategies like FHS or XDG
40    pub project_root: Option<PathBuf>,
41    /// Runtime files are similar to the cache, but don't persist between session/reboot
42    /// **NOTE**: May be missing in some strategies
43    pub runtime: Option<PathBuf>,
44    /// Non-essential data files that should persist between sessions. E.g. logs, history
45    pub state: PathBuf,
46}
47
48impl ProjectDirs {
49    pub fn new(dirs: HashMap<Directory, PathBuf>) -> Self {
50        Self(dirs)
51    }
52
53    pub fn empty() -> Self {
54        Self(HashMap::new())
55    }
56}
57
58impl From<FullProjectDirs> for ProjectDirs {
59    fn from(value: FullProjectDirs) -> Self {
60        let mut result = ProjectDirs::new(HashMap::from([
61            (Directory::Cache, value.cache),
62            (Directory::Config, value.config),
63            (Directory::Data, value.data),
64            (Directory::State, value.state),
65            (Directory::Log, value.log),
66            (Directory::Bin, value.bin),
67            (Directory::Lib, value.lib),
68            (Directory::Include, value.include),
69        ]));
70
71        if let Some(runtime) = value.runtime {
72            result.0.insert(Directory::Runtime, runtime);
73        }
74
75        if let Some(project_root) = value.project_root {
76            result.0.insert(Directory::ProjectRoot, project_root);
77        }
78
79        result
80    }
81}
82
83impl TryFrom<ProjectDirs> for FullProjectDirs {
84    type Error = MissingError;
85
86    fn try_from(mut value: ProjectDirs) -> Result<Self, Self::Error> {
87        let mut errors = Vec::<Directory>::new();
88
89        for dir in [
90            Directory::Cache,
91            Directory::Config,
92            Directory::Data,
93            Directory::State,
94            Directory::Log,
95            Directory::Bin,
96            Directory::Lib,
97        ] {
98            if !value.0.contains_key(&dir) {
99                errors.push(dir);
100            }
101        }
102
103        if errors.is_empty() {
104            Ok(FullProjectDirs {
105                cache: value.0.remove(&Directory::Cache).unwrap(),
106                config: value.0.remove(&Directory::Config).unwrap(),
107                data: value.0.remove(&Directory::Data).unwrap(),
108                state: value.0.remove(&Directory::State).unwrap(),
109                log: value.0.remove(&Directory::Log).unwrap(),
110                bin: value.0.remove(&Directory::Bin).unwrap(),
111                include: value.0.remove(&Directory::Include).unwrap(),
112                lib: value.0.remove(&Directory::Lib).unwrap(),
113                runtime: value.0.remove(&Directory::Runtime),
114                project_root: value.0.remove(&Directory::ProjectRoot),
115            })
116        } else {
117            Err(errors)
118        }
119    }
120}