1use std::sync::Arc;
2
3use anyhow::{Context, Result};
4use dashmap::DashMap;
5use globset::{Glob, GlobMatcher};
6use once_cell::sync::Lazy;
7use rustc_hash::FxBuildHasher;
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone)]
11pub struct CachedGlob {
12 glob: Arc<GlobMatcher>,
13}
14
15impl CachedGlob {
16 pub fn new(glob_str: &str) -> Result<Self> {
17 static CACHE: Lazy<DashMap<String, Arc<GlobMatcher>, FxBuildHasher>> =
18 Lazy::new(Default::default);
19
20 if let Some(cache) = CACHE.get(glob_str).as_deref().cloned() {
21 return Ok(Self { glob: cache });
22 }
23
24 let glob = Glob::new(glob_str)
25 .with_context(|| format!("failed to create glob for '{glob_str}'"))?
26 .compile_matcher();
27
28 let glob = Arc::new(glob);
29
30 CACHE.insert(glob_str.to_string(), glob.clone());
31
32 Ok(Self { glob })
33 }
34
35 pub fn is_match(&self, path: &str) -> bool {
36 self.glob.is_match(path)
37 }
38}
39
40impl Serialize for CachedGlob {
41 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
42 where
43 S: serde::Serializer,
44 {
45 String::serialize(&self.glob.glob().to_string(), serializer)
46 }
47}
48
49impl<'de> Deserialize<'de> for CachedGlob {
50 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
51 where
52 D: serde::Deserializer<'de>,
53 {
54 use serde::de::Error;
55
56 let glob = String::deserialize(deserializer)?;
57
58 Self::new(&glob).map_err(|err| D::Error::custom(err.to_string()))
59 }
60}