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 | |