fontconfig_parser/types/
document.rs1use crate::parser::parse_config;
2use crate::*;
3
4use std::collections::{BinaryHeap, 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 config_paths = 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::<BinaryHeap<_>>();
118
119 for config_path in config_paths {
120 match self.merge_config(&config_path) {
121 Ok(_) => {}
122 #[allow(unused_variables)]
123 Err(err) => {
124 #[cfg(feature = "log")]
125 log::warn!("Failed to merge {}: {}", config_path.display(), err);
126 }
127 }
128 }
129 }
130
131 Ok(())
132 }
133}
134
135macro_rules! define_config_part_from {
136 ($($f:ident,)+) => {
137 $(
138 impl From<$f> for ConfigPart {
139 fn from(v: $f) -> Self {
140 ConfigPart::$f(v)
141 }
142 }
143 )+
144 };
145}
146
147define_config_part_from! {
148 SelectFont,
149 Dir,
150 CacheDir,
151 Include,
152 Match,
153 Config,
154 Alias,
155 RemapDir,
156}
157
158#[derive(Clone, Debug, Default, PartialEq, Eq)]
159#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
160pub struct DirData {
162 pub path: PathBuf,
164 pub salt: String,
166}
167
168#[derive(Clone, Debug, Default, PartialEq, Eq)]
169#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
170pub struct RemapDirData {
172 pub path: PathBuf,
174 pub salt: String,
176 pub as_path: String,
178}