swc_cached/
regex.rs

1//! Regex cache
2
3use std::{ops::Deref, str::FromStr, sync::Arc};
4
5pub use anyhow::Error;
6use anyhow::{Context, Result};
7use dashmap::DashMap;
8use once_cell::sync::Lazy;
9use regex::Regex;
10use rustc_hash::FxBuildHasher;
11use serde::{Deserialize, Serialize};
12
13/// A regex which can be used as a configuration.
14///
15/// While deserializing, this will be converted to a string and cached based on
16/// the pattern.
17#[derive(Debug, Clone)]
18pub struct CachedRegex {
19    regex: Arc<Regex>,
20}
21
22impl Deref for CachedRegex {
23    type Target = Regex;
24
25    fn deref(&self) -> &Self::Target {
26        &self.regex
27    }
28}
29
30impl CachedRegex {
31    /// Get or create a cached regex. This will return the previous instance if
32    /// it's already cached.
33    pub fn new(input: &str) -> Result<Self> {
34        static CACHE: Lazy<DashMap<String, Arc<Regex>, FxBuildHasher>> =
35            Lazy::new(Default::default);
36
37        if let Some(cache) = CACHE.get(input).as_deref().cloned() {
38            return Ok(Self { regex: cache });
39        }
40
41        let regex =
42            Regex::new(input).with_context(|| format!("failed to parse `{}` as regex", input))?;
43        let regex = Arc::new(regex);
44
45        CACHE.insert(input.to_owned(), regex.clone());
46
47        Ok(CachedRegex { regex })
48    }
49}
50
51impl<'de> Deserialize<'de> for CachedRegex {
52    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
53    where
54        D: serde::Deserializer<'de>,
55    {
56        use serde::de::Error;
57
58        let s = String::deserialize(deserializer)?;
59
60        Self::new(&s).map_err(|err| D::Error::custom(err.to_string()))
61    }
62}
63
64impl Serialize for CachedRegex {
65    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
66    where
67        S: serde::Serializer,
68    {
69        let s = self.regex.as_str();
70
71        serializer.serialize_str(s)
72    }
73}
74
75/// This will panic for wrong patterns.
76impl From<&'_ str> for CachedRegex {
77    fn from(s: &'_ str) -> Self {
78        Self::new(s).unwrap()
79    }
80}
81
82impl FromStr for CachedRegex {
83    type Err = Error;
84
85    fn from_str(s: &str) -> Result<Self, Self::Err> {
86        Self::new(s)
87    }
88}