Skip to main content

upstream_rs/utils/
static_paths.rs

1use anyhow::{Context, Result};
2use std::path::PathBuf;
3
4/// Root directories for the application
5pub struct AppDirs {
6    pub user_dir: PathBuf,
7    pub config_dir: PathBuf,
8    pub data_dir: PathBuf,
9    pub packages_dir: PathBuf,
10    pub cache_dir: PathBuf,
11    pub metadata_dir: PathBuf,
12}
13
14impl Default for AppDirs {
15    fn default() -> Self {
16        Self::new().expect("failed to determine upstream app directories")
17    }
18}
19
20impl AppDirs {
21    pub fn new() -> Result<Self> {
22        let user_dir =
23            dirs::home_dir().context("Failed to determine the current user's home directory")?;
24        let config_dir = dirs::config_dir()
25            .context("Failed to determine the current user's config directory")?
26            .join("upstream");
27
28        let data_dir = user_dir.join(".upstream");
29        let packages_dir = data_dir.join("packages");
30        let cache_dir = data_dir.join("cache");
31        let metadata_dir = data_dir.join("metadata");
32
33        Ok(Self {
34            user_dir,
35            config_dir,
36            data_dir,
37            packages_dir,
38            cache_dir,
39            metadata_dir,
40        })
41    }
42}
43
44/// Paths to configuration and metadata files
45pub struct ConfigPaths {
46    pub config_file: PathBuf,
47    pub packages_file: PathBuf,
48    pub metadata_file: PathBuf,
49    pub trust_file: PathBuf,
50    pub paths_file: PathBuf,
51    pub paths_nu_file: PathBuf,
52}
53
54impl ConfigPaths {
55    pub fn new(dirs: &AppDirs) -> Self {
56        Self {
57            config_file: dirs.config_dir.join("config.toml"),
58            packages_file: dirs.metadata_dir.join("packages.json"),
59            metadata_file: dirs.metadata_dir.join("metadata.json"),
60            trust_file: dirs.metadata_dir.join("trust.json"),
61            paths_file: dirs.metadata_dir.join("paths.sh"),
62            paths_nu_file: dirs.metadata_dir.join("paths.nu"),
63        }
64    }
65}
66
67/// Directories where packages are installed
68pub struct InstallPaths {
69    pub appimages_dir: PathBuf,
70    pub binaries_dir: PathBuf,
71    pub archives_dir: PathBuf,
72    pub rollback_dir: PathBuf,
73    pub tmp_dir: PathBuf,
74}
75
76impl InstallPaths {
77    pub fn new(dirs: &AppDirs) -> Self {
78        Self {
79            appimages_dir: dirs.packages_dir.join("appimages"),
80            binaries_dir: dirs.packages_dir.join("binaries"),
81            archives_dir: dirs.packages_dir.join("archives"),
82            rollback_dir: dirs.data_dir.join("rollback"),
83            tmp_dir: dirs.data_dir.join("temp"),
84        }
85    }
86}
87
88/// Paths for system integration (symlinks, XDG dirs)
89pub struct IntegrationPaths {
90    pub symlinks_dir: PathBuf,
91    pub xdg_applications_dir: PathBuf,
92    pub icons_dir: PathBuf,
93    pub bash_completions_dir: PathBuf,
94    pub fish_completions_dir: PathBuf,
95    pub zsh_completions_dir: PathBuf,
96}
97
98impl IntegrationPaths {
99    pub fn new(dirs: &AppDirs) -> Self {
100        Self {
101            symlinks_dir: dirs.data_dir.join("symlinks"),
102            icons_dir: dirs.data_dir.join("icons"),
103            xdg_applications_dir: dirs.user_dir.join(".local/share/applications"),
104            bash_completions_dir: dirs
105                .user_dir
106                .join(".local/share/bash-completion/completions"),
107            fish_completions_dir: dirs.user_dir.join(".config/fish/completions"),
108            zsh_completions_dir: dirs.user_dir.join(".local/share/zsh/site-functions"),
109        }
110    }
111}
112
113/// Convenience wrapper that holds all path types
114pub struct UpstreamPaths {
115    pub dirs: AppDirs,
116    pub config: ConfigPaths,
117    pub install: InstallPaths,
118    pub integration: IntegrationPaths,
119}
120
121impl Default for UpstreamPaths {
122    fn default() -> Self {
123        Self::new().expect("failed to determine upstream paths")
124    }
125}
126
127impl UpstreamPaths {
128    pub fn new() -> Result<Self> {
129        let dirs = AppDirs::new()?;
130        Ok(Self {
131            config: ConfigPaths::new(&dirs),
132            install: InstallPaths::new(&dirs),
133            integration: IntegrationPaths::new(&dirs),
134            dirs,
135        })
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use super::UpstreamPaths;
142
143    #[test]
144    fn upstream_paths_are_composed_from_base_directories() {
145        let paths = UpstreamPaths::new().expect("paths");
146
147        assert_eq!(
148            paths.config.config_file,
149            paths.dirs.config_dir.join("config.toml")
150        );
151        assert_eq!(
152            paths.config.packages_file,
153            paths.dirs.metadata_dir.join("packages.json")
154        );
155        assert_eq!(
156            paths.config.metadata_file,
157            paths.dirs.metadata_dir.join("metadata.json")
158        );
159        assert_eq!(
160            paths.config.trust_file,
161            paths.dirs.metadata_dir.join("trust.json")
162        );
163        assert_eq!(
164            paths.config.paths_nu_file,
165            paths.dirs.metadata_dir.join("paths.nu")
166        );
167        assert_eq!(
168            paths.install.binaries_dir,
169            paths.dirs.packages_dir.join("binaries")
170        );
171        assert_eq!(
172            paths.dirs.packages_dir,
173            paths.dirs.data_dir.join("packages")
174        );
175        assert_eq!(paths.dirs.cache_dir, paths.dirs.data_dir.join("cache"));
176        assert_eq!(
177            paths.integration.symlinks_dir,
178            paths.dirs.data_dir.join("symlinks")
179        );
180        assert_eq!(
181            paths.integration.fish_completions_dir,
182            paths.dirs.user_dir.join(".config/fish/completions")
183        );
184        assert_eq!(
185            paths.install.rollback_dir,
186            paths.dirs.data_dir.join("rollback")
187        );
188        assert_eq!(paths.install.tmp_dir, paths.dirs.data_dir.join("temp"));
189    }
190}