litcheck_core/fs/
glob.rs

1use std::{collections::BTreeSet, fmt, ops::Deref, path::Path, str::FromStr};
2
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4
5/// Represents a glob pattern which can match strings or paths
6#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
7pub struct Pattern(glob::Pattern);
8impl fmt::Display for Pattern {
9    #[inline]
10    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
11        fmt::Display::fmt(&self.0, f)
12    }
13}
14impl FromStr for Pattern {
15    type Err = <glob::Pattern as FromStr>::Err;
16
17    #[inline]
18    fn from_str(s: &str) -> Result<Self, Self::Err> {
19        s.parse().map(Self)
20    }
21}
22impl Deref for Pattern {
23    type Target = glob::Pattern;
24
25    #[inline(always)]
26    fn deref(&self) -> &Self::Target {
27        &self.0
28    }
29}
30impl Serialize for Pattern {
31    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
32    where
33        S: Serializer,
34    {
35        serializer.serialize_str(self.as_str())
36    }
37}
38impl<'de> Deserialize<'de> for Pattern {
39    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
40    where
41        D: Deserializer<'de>,
42    {
43        use serde::de::Visitor;
44
45        struct PatternVisitor;
46        impl<'de> Visitor<'de> for PatternVisitor {
47            type Value = Pattern;
48
49            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
50                formatter.write_str("a valid glob pattern")
51            }
52
53            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
54            where
55                E: serde::de::Error,
56            {
57                s.parse::<Pattern>().map_err(serde::de::Error::custom)
58            }
59        }
60
61        deserializer.deserialize_str(PatternVisitor)
62    }
63}
64
65/// Represents a set of glob patterns which are applied as a unit
66#[derive(Default, Debug, Clone, Serialize, Deserialize)]
67pub struct PatternSet(BTreeSet<Pattern>);
68impl PatternSet {
69    /// Returns true if there are no patterns in this set
70    pub fn is_empty(&self) -> bool {
71        self.0.is_empty()
72    }
73
74    pub fn iter(&self) -> impl Iterator<Item = &Pattern> + '_ {
75        self.0.iter()
76    }
77
78    /// Returns true if any pattern in the set matches `s`
79    pub fn matches(&self, s: &str) -> bool {
80        self.0.iter().any(|pattern| pattern.matches(s))
81    }
82
83    /// Returns true if any pattern in the set matches `path`
84    pub fn matches_path(&self, path: &Path) -> bool {
85        self.0.iter().any(|pattern| pattern.matches_path(path))
86    }
87}
88impl FromIterator<Pattern> for PatternSet {
89    fn from_iter<T>(iter: T) -> Self
90    where
91        T: IntoIterator<Item = Pattern>,
92    {
93        Self(iter.into_iter().collect())
94    }
95}
96impl<'a> FromIterator<&'a Pattern> for PatternSet {
97    fn from_iter<T>(iter: T) -> Self
98    where
99        T: IntoIterator<Item = &'a Pattern>,
100    {
101        Self(iter.into_iter().cloned().collect())
102    }
103}