flams_math_archives/utils/
ignore_source.rs

1use regex::Regex;
2use std::path::{Path, PathBuf};
3
4use crate::utils::path_ext::PathExt;
5
6/// A regular expression (using [`Regex`]) used to ignore source files specified as
7/// relative to the source directory of some [`MathArchive`](crate::MathArchive)
8///
9/// # Example
10/// ```
11/// # use flams_math_archives::utils::ignore_source::IgnoreSource;
12/// # use std::path::Path;
13/// // The source directory of an archive:
14/// let source_path = Path::new("/home/user/MathHub/FTML/doc/source");
15/// let ignore = IgnoreSource::new("*/code/*|*/tikz/*|*/tutorial/solution/*", source_path);
16/// let path = Path::new("/home/user/MathHub/FTML/doc/source/tutorial/solution/preamble.tex");
17/// assert!(ignore.ignores(path));
18/// let path = Path::new("/home/user/MathHub/FTML/doc/source/tutorial/math/assertions.en.tex");
19/// assert!(!ignore.ignores(path));
20/// ```
21#[derive(Default, Clone, Debug)]
22pub struct IgnoreSource(Option<Regex>);
23impl std::fmt::Display for IgnoreSource {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        match &self.0 {
26            Some(r) => r.fmt(f),
27            None => write!(f, "(None)"),
28        }
29    }
30}
31
32impl PartialEq for IgnoreSource {
33    fn eq(&self, other: &Self) -> bool {
34        match (&self.0, &other.0) {
35            (Some(a), Some(b)) => a.as_str() == b.as_str(),
36            (None, None) => true,
37            _ => false,
38        }
39    }
40}
41
42impl IgnoreSource {
43    #[must_use]
44    pub fn new(regex: &str, source_path: &Path) -> Self {
45        if regex.is_empty() {
46            return Self::default();
47        }
48        //#[cfg(target_os = "windows")]
49        //let regex = regex.replace('/', PathBuf::PATH_SEPARATOR);
50        let s = regex.replace('.', r"\.").replace('*', ".*");
51        let s = s
52            .split('|')
53            .filter(|s| !s.is_empty())
54            .collect::<Vec<_>>()
55            .join("|");
56        let p = source_path.display();
57        #[cfg(target_os = "windows")]
58        let p = p.to_string()[3..].replace(PathBuf::PATH_SEPARATOR,"/");
59        let s = format!("{p}(/)?({s})");
60        Self(Regex::new(&s).ok())
61    }
62
63    #[must_use]
64    pub fn ignores(&self, p: &Path) -> bool {
65        let Some(p) = p.to_str() else { return false };
66        if cfg!(target_os = "windows") {
67            let p = p[3..].replace(PathBuf::PATH_SEPARATOR,"/");
68            self.0.as_ref().is_some_and(|r| r.is_match(&p))
69        } else {
70            self.0.as_ref().is_some_and(|r| r.is_match(p))
71        }
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78    fn get_ignore(source: &Path) -> IgnoreSource {
79        IgnoreSource::new("*/code/*|*/tikz/*|*/tutorial/solution/*", source)
80    }
81
82    #[test]
83    fn ignore_test() {
84        let source = Path::new(if cfg!(target_os = "windows") {"C:\\home\\user\\MathHub\\sTeX\\Documentation\\source"} else {"/home/user/MathHub/sTeX/Documentation/source"});
85        let ignore = get_ignore(source);
86        let path = Path::new(
87            if cfg!(target_os = "windows") {
88                "c:\\home\\user\\MathHub\\sTeX\\Documentation\\source\\tutorial/solution\\preamble.tex"
89            } else {"/home/user/MathHub/sTeX/Documentation/source/tutorial/solution/preamble.tex"},
90        );
91        assert!(ignore.ignores(path),"{ignore} matches {}",path.display());
92        let path = Path::new(
93            if cfg!(target_os = "windows") {
94                "C:\\home\\user\\MathHub\\sTeX\\Documentation\\source\\tutorial\\math\\assertions.en.tex"
95            } else {"/home/user/MathHub/sTeX/Documentation/source/tutorial/math/assertions.en.tex"},
96        );
97        assert!(!ignore.ignores(path),"{ignore} matches {}",path.display());
98    }
99}