fontconfig_parser/types/
dir.rs

1#[derive(Clone, Debug, Default, PartialEq, Eq)]
2#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3pub 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))]
11pub 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))]
18pub 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))]
29pub 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))]
38pub enum DirPrefix {
39    Default,
40    Cwd,
41    Xdg,
42    Relative,
43}
44
45pub enum PrefixBehavior {
46    Config,
47    Cwd,
48    Xdg,
49    Relative,
50}
51
52parse_enum! {
53    DirPrefix,
54    (Default, "default"),
55    (Cwd, "cwd"),
56    (Xdg, "xdg"),
57    (Relative, "relative"),
58}
59
60impl 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)]
71fn config_home() -> Result<String, std::env::VarError> {
72    let mut home = std::env::var("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.
87fn 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(p)
93    }
94}
95
96fn expand_tilde(path: &String) -> std::path::PathBuf {
97    let parsed_path = std::path::Path::new(path);
98    if let Ok(stripped_path) = parsed_path.strip_prefix("~") {
99        let home = config_home().unwrap_or("/".to_string());
100        std::path::Path::new(&home).join(stripped_path)
101    } else {
102        parsed_path.into()
103    }
104}
105
106macro_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
155define_calculate_path!(Dir, "XDG_DATA_HOME", "~/.local/share", PrefixBehavior::Cwd);
156define_calculate_path!(CacheDir, "XDG_CACHE_HOME", "~/.cache", PrefixBehavior::Cwd);
157define_calculate_path!(
158    Include,
159    "XDG_CONFIG_HOME",
160    "~/.config",
161    PrefixBehavior::Config
162);
163define_calculate_path!(
164    RemapDir,
165    "XDG_CONFIG_HOME",
166    "~/.config",
167    PrefixBehavior::Cwd
168);