fontconfig_parser/types/
document.rs
1use crate::parser::parse_config;
2use crate::*;
3
4use std::collections::HashSet;
5use std::fs;
6use std::path::{Path, PathBuf};
7
8#[derive(Clone, Debug, PartialEq)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub enum ConfigPart {
11 Description(String),
12 SelectFont(SelectFont),
13 Dir(Dir),
14 CacheDir(CacheDir),
15 Include(Include),
16 Match(Match),
17 Config(Config),
18 Alias(Alias),
19 RemapDir(RemapDir),
20 ResetDirs,
21}
22
23#[derive(Clone, Debug, Default, PartialEq)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25pub struct FontConfig {
26 pub select_fonts: Vec<SelectFont>,
27 pub dirs: Vec<DirData>,
28 pub cache_dirs: Vec<PathBuf>,
29 pub remap_dirs: Vec<RemapDirData>,
30 pub matches: Vec<Match>,
31 pub config: Config,
32 pub aliases: Vec<Alias>,
33 pub config_files: HashSet<PathBuf>,
34}
35
36impl FontConfig {
37 pub fn merge_config<P: AsRef<Path> + ?Sized>(&mut self, config_path: &P) -> Result<()> {
38 match std::fs::canonicalize(&config_path) {
39 Ok(p) => {
40 if !self.config_files.insert(std::path::PathBuf::from(p)) {
41 return Ok(());
42 }
43 }
44 Err(err) => return Err(Error::IoError(err)),
45 }
46
47 let config = fs::read_to_string(config_path.as_ref())?;
48 let xml_doc = roxmltree::Document::parse_with_options(
49 &config,
50 roxmltree::ParsingOptions {
51 allow_dtd: true,
52 ..Default::default()
53 },
54 )?;
55
56 for part in parse_config(&xml_doc)? {
57 match part? {
58 ConfigPart::Alias(alias) => self.aliases.push(alias),
59 ConfigPart::Config(mut c) => {
60 self.config.rescans.append(&mut c.rescans);
61 self.config.blanks.append(&mut c.blanks);
62 }
63 ConfigPart::Description(_) => {}
64 ConfigPart::Dir(dir) => self.dirs.push(DirData {
65 path: dir.calculate_path(config_path),
66 salt: dir.salt,
67 }),
68 ConfigPart::CacheDir(dir) => self.cache_dirs.push(dir.calculate_path(config_path)),
69 ConfigPart::Match(m) => self.matches.push(m),
70 ConfigPart::ResetDirs => self.dirs.clear(),
71 ConfigPart::SelectFont(s) => self.select_fonts.push(s),
72 ConfigPart::RemapDir(remap) => self.remap_dirs.push(RemapDirData {
73 path: remap.calculate_path(config_path),
74 salt: remap.salt,
75 as_path: remap.as_path,
76 }),
77 ConfigPart::Include(dir) => {
78 let include_path = dir.calculate_path(config_path);
79
80 match self.include(&include_path) {
81 Ok(_) => {}
82 #[allow(unused_variables)]
83 Err(err) => {
84 if !dir.ignore_missing {
85 #[cfg(feature = "log")]
86 log::warn!("Failed to include {}: {}", include_path.display(), err);
87 }
88 }
89 }
90 }
91 }
92 }
93
94 Ok(())
95 }
96
97 fn include(&mut self, include_path: &Path) -> Result<()> {
98 let meta = fs::metadata(include_path)?;
99 let ty = meta.file_type();
100
101 if ty.is_file() {
103 self.merge_config(include_path)?;
104 } else if ty.is_dir() {
105 let dir = std::fs::read_dir(include_path)?;
106 let mut config_paths: Vec<_> = dir
107 .filter_map(|entry| {
108 let entry = entry.ok()?;
109 let ty = entry.file_type().ok()?;
110
111 if ty.is_file() || ty.is_symlink() {
112 Some(entry.path())
113 } else {
114 None
115 }
116 })
117 .collect();
118
119 config_paths.sort();
123
124 for config_path in config_paths {
125 match self.merge_config(&config_path) {
126 Ok(_) => {}
127 #[allow(unused_variables)]
128 Err(err) => {
129 #[cfg(feature = "log")]
130 log::warn!("Failed to merge {}: {}", config_path.display(), err);
131 }
132 }
133 }
134 }
135
136 Ok(())
137 }
138}
139
140macro_rules! define_config_part_from {
141 ($($f:ident,)+) => {
142 $(
143 impl From<$f> for ConfigPart {
144 fn from(v: $f) -> Self {
145 ConfigPart::$f(v)
146 }
147 }
148 )+
149 };
150}
151
152define_config_part_from! {
153 SelectFont,
154 Dir,
155 CacheDir,
156 Include,
157 Match,
158 Config,
159 Alias,
160 RemapDir,
161}
162
163#[derive(Clone, Debug, Default, PartialEq, Eq)]
164#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
165pub struct DirData {
167 pub path: PathBuf,
169 pub salt: String,
171}
172
173#[derive(Clone, Debug, Default, PartialEq, Eq)]
174#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
175pub struct RemapDirData {
177 pub path: PathBuf,
179 pub salt: String,
181 pub as_path: String,
183}