Skip to main content

glob_set/
lib.rs

1#![doc = include_str!("../README.md")]
2#![no_std]
3extern crate alloc;
4
5mod engine;
6mod error;
7mod glob;
8mod literal;
9mod map;
10mod parse;
11mod set;
12mod strategy;
13mod tinyset;
14
15pub use crate::error::{Error, ErrorKind};
16pub use crate::glob::{Candidate, Glob, GlobBuilder, GlobMatcher};
17pub use crate::map::{GlobMap, GlobMapBuilder};
18pub use crate::set::{GlobSet, GlobSetBuilder};
19pub use crate::tinyset::{TinyGlobSet, TinyGlobSetBuilder};
20
21use alloc::string::String;
22
23/// Escape all special glob characters in the given string.
24///
25/// The returned string, when used as a glob pattern, will match the input
26/// string literally.
27pub fn escape(s: &str) -> String {
28    let mut escaped = String::with_capacity(s.len());
29    for c in s.chars() {
30        match c {
31            '*' | '?' | '[' | ']' | '{' | '}' | '\\' | '!' | '^' | ',' => {
32                escaped.push('\\');
33                escaped.push(c);
34            }
35            _ => escaped.push(c),
36        }
37    }
38    escaped
39}
40
41#[cfg(test)]
42#[allow(clippy::unwrap_used)]
43mod tests {
44    use super::*;
45
46    #[test]
47    fn escape_special_chars() {
48        assert_eq!(escape("*.rs"), "\\*.rs");
49        assert_eq!(escape("[foo]"), "\\[foo\\]");
50        assert_eq!(escape("{a,b}"), "\\{a\\,b\\}");
51        assert_eq!(escape("a\\b"), "a\\\\b");
52        assert_eq!(escape("!negated"), "\\!negated");
53    }
54
55    #[test]
56    fn escape_round_trip() {
57        let original = "hello*world?[test]{a,b}";
58        let escaped = escape(original);
59        let glob = Glob::new(&escaped).unwrap();
60        let matcher = glob.compile_matcher();
61        assert!(matcher.is_match(original));
62    }
63
64    #[test]
65    fn escape_no_special() {
66        assert_eq!(escape("hello.txt"), "hello.txt");
67        assert_eq!(escape("src/main.rs"), "src/main.rs");
68    }
69}