sub_strs/
glob_set.rs

1/*
2==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--
3
4sub-strs
5
6Copyright (C) 2019-2024  Anonymous
7
8There are several releases over multiple years,
9they are listed as ranges, such as: "2019-2024".
10
11This program is free software: you can redistribute it and/or modify
12it under the terms of the GNU Lesser General Public License as published by
13the Free Software Foundation, either version 3 of the License, or
14(at your option) any later version.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19GNU Lesser General Public License for more details.
20
21You should have received a copy of the GNU Lesser General Public License
22along with this program.  If not, see <https://www.gnu.org/licenses/>.
23
24::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
25*/
26
27//! # Glob set
28
29use {
30    alloc::{
31        borrow::Cow,
32        collections::BTreeSet,
33    },
34    crate::Glob,
35};
36
37/// # Glob set
38///
39/// This struct contains a set of [`Glob`][struct:Glob]'s.
40///
41/// [struct:Glob]: struct.Glob.html
42#[derive(Debug, Eq, PartialEq)]
43pub struct GlobSet<'a> {
44
45    globs: BTreeSet<Glob<'a>>,
46
47}
48
49impl<'a> GlobSet<'a> {
50
51    /// # Makes new instance from an iterator of [`Cow<'a, str>`][r::Cow]
52    ///
53    /// ## Notes
54    ///
55    /// - Empty strings will be ignored.
56    /// - If the iterator contains no globs, `None` is returned.
57    ///
58    /// ## Examples
59    ///
60    /// ```
61    /// use sub_strs::GlobSet;
62    ///
63    /// let glob_set = GlobSet::from_iter("*.rs|*.md".split('|')).unwrap();
64    /// assert!(glob_set.any("this.rs"));
65    /// assert!(glob_set.any("that.md"));
66    /// assert!(glob_set.any("not-this") == false);
67    /// ```
68    ///
69    /// [r::Cow]: https://doc.rust-lang.org/std/borrow/enum.Cow.html
70    pub fn from_iter<I0, I1>(iter: I0) -> Option<Self> where I0: IntoIterator<Item=I1>, I1: Into<Cow<'a, str>> {
71        let globs: BTreeSet<_> = iter.into_iter().filter_map(|s| {
72            let s = s.into();
73            (s.is_empty() == false).then(|| Glob::from(s))
74        }).collect();
75        (globs.is_empty() == false).then_some(Self { globs })
76    }
77
78    /// # Merges an iterator of [`GlobSet<'a>`][::GlobSet] into one
79    ///
80    /// ## Notes
81    ///
82    /// - If there are no inner [`Glob`][::Glob]'s, `None` is returned.
83    /// - For `#![no_std]` users: this function takes little effort to remove duplicates. You should help with that yourself (by not providing
84    ///   duplicates).
85    ///
86    /// ## Examples
87    ///
88    /// ```
89    /// use sub_strs::GlobSet;
90    ///
91    /// let program_args = &["*.svg,*.OGG", "*.md"];
92    /// let glob_set = GlobSet::merge(
93    ///     program_args.iter().filter_map(|s|
94    ///         GlobSet::from_iter(s.to_lowercase().split(',').map(|s| s.to_string()))
95    ///     )
96    /// );
97    /// assert!(glob_set.map(|g| g.any("some.ogg")).unwrap_or(false));
98    /// ```
99    ///
100    /// [::Glob]: struct.Glob.html
101    /// [::GlobSet]: struct.GlobSet.html
102    pub fn merge<I>(iter: I) -> Option<Self> where I: IntoIterator<Item=Self> {
103        let result = iter.into_iter().fold(Self { globs: BTreeSet::new() }, |mut result, mut item| {
104            result.globs.append(&mut item.globs);
105            result
106        });
107        (result.globs.is_empty() == false).then_some(result)
108    }
109
110    /// # Checks if _any_ glob inside matches the input string
111    pub fn any<S>(&self, s: S) -> bool where S: AsRef<str> {
112        self.globs.iter().any(|g| g.matches(&s))
113    }
114
115}