1 | #[derive (Clone, Debug, Default, PartialEq, Eq)] |
2 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))] |
3 | pub struct Dir { |
4 | pub prefix: DirPrefix, |
5 | pub salt: String, |
6 | pub path: String, |
7 | } |
8 | |
9 | #[derive (Clone, Debug, Default, PartialEq, Eq)] |
10 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))] |
11 | pub struct CacheDir { |
12 | pub prefix: DirPrefix, |
13 | pub path: String, |
14 | } |
15 | |
16 | #[derive (Clone, Debug, Default, PartialEq, Eq)] |
17 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))] |
18 | pub struct Include { |
19 | pub prefix: DirPrefix, |
20 | pub ignore_missing: bool, |
21 | pub path: String, |
22 | } |
23 | |
24 | /// This element contains a directory name where will be mapped as the path 'as-path' in cached information. This is useful if the directory name is an alias (via a bind mount or symlink) to another directory in the system for which cached font information is likely to exist. |
25 | |
26 | /// 'salt' property affects to determine cache filename as same as [`Dir`] element. |
27 | #[derive (Clone, Debug, Default, PartialEq, Eq)] |
28 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))] |
29 | pub struct RemapDir { |
30 | pub prefix: DirPrefix, |
31 | pub as_path: String, |
32 | pub salt: String, |
33 | pub path: String, |
34 | } |
35 | |
36 | #[derive (Clone, Copy, Debug, PartialEq, Eq)] |
37 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))] |
38 | pub enum DirPrefix { |
39 | Default, |
40 | Cwd, |
41 | Xdg, |
42 | Relative, |
43 | } |
44 | |
45 | pub enum PrefixBehavior { |
46 | Config, |
47 | Cwd, |
48 | Xdg, |
49 | Relative, |
50 | } |
51 | |
52 | parse_enum! { |
53 | DirPrefix, |
54 | (Default, "default" ), |
55 | (Cwd, "cwd" ), |
56 | (Xdg, "xdg" ), |
57 | (Relative, "relative" ), |
58 | } |
59 | |
60 | impl Default for DirPrefix { |
61 | fn default() -> Self { |
62 | DirPrefix::Default |
63 | } |
64 | } |
65 | |
66 | /// Get the location to user home directory. |
67 | /// |
68 | /// This implementation follows `FcConfigHome` function of freedesktop.org's |
69 | /// Fontconfig library. |
70 | #[allow (unused_mut, clippy::let_and_return)] |
71 | fn config_home() -> Result<String, std::env::VarError> { |
72 | let mut home: Result = std::env::var(key:"HOME" ); |
73 | |
74 | #[cfg (target_os = "windows" )] |
75 | { |
76 | home = home.or_else(|_| std::env::var("USERPROFILE" )); |
77 | } |
78 | |
79 | home |
80 | } |
81 | |
82 | /// Given a relative path to a config file, this function returns |
83 | /// the complete file name to load. |
84 | /// |
85 | /// This is a simplified version of `FcConfigGetFilename` from the Fontconfig |
86 | /// library. |
87 | fn config_get_file_name(p: &std::path::PathBuf) -> std::path::PathBuf { |
88 | if cfg!(target_os = "windows" ) { |
89 | // TODO: get config file path properly for Windows |
90 | return p.clone(); |
91 | } else { |
92 | std::path::Path::new("/etc/fonts" ).join(path:p) |
93 | } |
94 | } |
95 | |
96 | fn expand_tilde(path: &String) -> std::path::PathBuf { |
97 | let parsed_path: &Path = std::path::Path::new(path); |
98 | if let Ok(stripped_path: &Path) = parsed_path.strip_prefix(base:"~" ) { |
99 | let home: String = config_home().unwrap_or(default:"/" .to_string()); |
100 | std::path::Path::new(&home).join(stripped_path) |
101 | } else { |
102 | parsed_path.into() |
103 | } |
104 | } |
105 | |
106 | macro_rules! define_calculate_path { |
107 | ($ty:ident, $xdg_env:expr, $xdg_fallback:expr, $default_prefix_behavior:expr) => { |
108 | impl $ty { |
109 | /// Environment variable name which used `xdg` prefix |
110 | pub const XDG_ENV: &'static str = $xdg_env; |
111 | /// Fallback path when `XDG_ENV` is not exists |
112 | pub const XDG_FALLBACK_PATH: &'static str = $xdg_fallback; |
113 | const DEFAULT_PREFIX_BEHAVIOR: PrefixBehavior = $default_prefix_behavior; |
114 | |
115 | fn get_prefix_behavior(prefix: DirPrefix) -> PrefixBehavior { |
116 | match prefix { |
117 | DirPrefix::Default => Self::DEFAULT_PREFIX_BEHAVIOR, |
118 | DirPrefix::Cwd => PrefixBehavior::Cwd, |
119 | DirPrefix::Xdg => PrefixBehavior::Xdg, |
120 | DirPrefix::Relative => PrefixBehavior::Relative, |
121 | } |
122 | } |
123 | |
124 | /// Calculate actual path |
125 | pub fn calculate_path<P: AsRef<std::path::Path> + ?Sized>( |
126 | &self, |
127 | config_file_path: &P, |
128 | ) -> std::path::PathBuf { |
129 | let expanded_path = expand_tilde(&self.path); |
130 | |
131 | if expanded_path.is_absolute() { |
132 | return expanded_path; |
133 | } |
134 | |
135 | let prefix = Self::get_prefix_behavior(self.prefix); |
136 | |
137 | match prefix { |
138 | PrefixBehavior::Config => config_get_file_name(&expanded_path), |
139 | PrefixBehavior::Cwd => std::path::Path::new("." ).join(expanded_path), |
140 | PrefixBehavior::Relative => match config_file_path.as_ref().parent() { |
141 | Some(parent) => parent.join(expanded_path), |
142 | None => std::path::Path::new("." ).join(expanded_path), |
143 | }, |
144 | PrefixBehavior::Xdg => { |
145 | let xdg_path = |
146 | std::env::var($xdg_env).unwrap_or_else(|_| $xdg_fallback.into()); |
147 | expand_tilde(&xdg_path).join(expanded_path) |
148 | } |
149 | } |
150 | } |
151 | } |
152 | }; |
153 | } |
154 | |
155 | define_calculate_path!(Dir, "XDG_DATA_HOME" , "~/.local/share" , PrefixBehavior::Cwd); |
156 | define_calculate_path!(CacheDir, "XDG_CACHE_HOME" , "~/.cache" , PrefixBehavior::Cwd); |
157 | define_calculate_path!( |
158 | Include, |
159 | "XDG_CONFIG_HOME" , |
160 | "~/.config" , |
161 | PrefixBehavior::Config |
162 | ); |
163 | define_calculate_path!( |
164 | RemapDir, |
165 | "XDG_CONFIG_HOME" , |
166 | "~/.config" , |
167 | PrefixBehavior::Cwd |
168 | ); |
169 | |