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}