1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
// License: see LICENSE file at root directory of main branch
//! # Glob set
use alloc::borrow::Cow;
#[cfg(not(feature="std"))]
use alloc::vec::Vec;
#[cfg(feature="std")]
use std::collections::HashSet;
use crate::Glob;
/// # Glob set
///
/// This struct contains a set of [`Glob`][::Glob]'s.
///
/// ## Notes
///
/// For `#![no_std]`: this struct uses a [vector][r:alloc:Vec] inside. Since at the time of implementation, [`HashSet`][r:std:HashSet] was not
/// available.
///
/// [::Glob]: struct.Glob.html
/// [r:alloc:Vec]: https://doc.rust-lang.org/alloc/vec/struct.Vec.html
/// [r:std:HashSet]: https://doc.rust-lang.org/std/collections/struct.HashSet.html
#[derive(Debug, Eq, PartialEq)]
pub struct GlobSet<'a> {
// We can't use BTreeSet, because it requires Ord/PartialOrd. Implementing them for Glob makes no sense.
#[cfg(not(feature="std"))]
globs: Vec<Glob<'a>>,
#[cfg(feature="std")]
globs: HashSet<Glob<'a>>,
}
impl<'a> GlobSet<'a> {
/// # Makes new instance from an iterator of [`Cow<'a, str>`][r::Cow]
///
/// ## Notes
///
/// - Empty strings will be ignored.
/// - If the iterator contains no globs, `None` is returned.
/// - For `#![no_std]` users: this function takes little effort to remove duplicates. You should help with that yourself (by not providing
/// duplicates).
///
/// ## Examples
///
/// ```
/// use sub_strs::GlobSet;
///
/// let glob_set = GlobSet::from_iter("*.rs|*.md".split('|').map(|s| s.into())).unwrap();
/// assert!(glob_set.any("this.rs"));
/// assert!(glob_set.any("that.md"));
/// assert!(glob_set.any("not-this") == false);
/// ```
///
/// [r::Cow]: https://doc.rust-lang.org/std/borrow/enum.Cow.html
pub fn from_iter<I>(strs: I) -> Option<Self> where I: Iterator<Item=Cow<'a, str>> {
#[cfg(feature="std")]
let globs: HashSet<_> = strs.filter_map(|s| match s.is_empty() {
true => None,
false => Some(Glob::from(s)),
}).collect();
#[cfg(not(feature="std"))]
let mut globs: Vec<_> = strs.filter_map(|s| match s.is_empty() {
true => None,
false => Some(Glob::from(s)),
}).collect();
#[cfg(not(feature="std"))]
globs.dedup();
match globs.is_empty() {
true => None,
false => Some(Self { globs })
}
}
/// # Merges an iterator of [`GlobSet<'a>`][::GlobSet] into one
///
/// ## Notes
///
/// - If there are no inner [`Glob`][::Glob]'s, `None` is returned.
/// - For `#![no_std]` users: this function takes little effort to remove duplicates. You should help with that yourself (by not providing
/// duplicates).
///
/// ## Examples
///
/// ```
/// use sub_strs::GlobSet;
///
/// let program_args = &["*.svg,*.OGG", "*.md"];
/// let glob_set = GlobSet::merge(
/// program_args.iter().filter_map(|s|
/// GlobSet::from_iter(s.to_lowercase().split(',').map(|s| s.to_string().into()))
/// )
/// );
/// assert!(glob_set.map(|g| g.any("some.ogg")).unwrap_or(false));
/// ```
///
/// [::Glob]: struct.Glob.html
/// [::GlobSet]: struct.GlobSet.html
pub fn merge<I>(iter: I) -> Option<Self> where I: Iterator<Item=Self> {
#[cfg(feature="std")]
let result = iter.fold(Self { globs: HashSet::new() }, |mut result, item| {
result.globs.extend(item.globs);
result
});
#[cfg(not(feature="std"))]
let mut result = iter.fold(Self { globs: Vec::new() }, |mut result, item| {
result.globs.extend(item.globs);
result
});
#[cfg(not(feature="std"))]
result.globs.dedup();
match result.globs.is_empty() {
true => None,
false => Some(result),
}
}
/// # Checks if _any_ glob inside matches the input string
pub fn any<S>(&self, s: S) -> bool where S: AsRef<str> {
self.globs.iter().any(|g| g.matches(&s))
}
}