jsonpath_rust/path/config/
cache.rs

1use regex::{Error, Regex};
2use serde_json::Value;
3use std::collections::HashMap;
4use std::sync::{Arc, Mutex, PoisonError};
5
6/// The option to provide a cache for regex
7/// ```
8/// use serde_json::json;
9/// use jsonpath_rust::JsonPathQuery;
10/// use jsonpath_rust::path::config::cache::{DefaultRegexCacheInst, RegexCache};
11/// use jsonpath_rust::path::config::JsonPathConfig;
12///
13/// let cfg = JsonPathConfig::new(RegexCache::Implemented(DefaultRegexCacheInst::default()));
14///     let json = Box::new(json!({
15///             "author":"abcd(Rees)",
16///         }));
17///
18///     let _v = (json, cfg).path("$.[?(@.author ~= '.*(?i)d\\(Rees\\)')]")
19///         .expect("the path is correct");
20#[derive(Clone)]
21pub enum RegexCache<T = DefaultRegexCacheInst>
22where
23    T: Clone + RegexCacheInst,
24{
25    Absent,
26    Implemented(T),
27}
28
29impl<T> RegexCache<T>
30where
31    T: Clone + RegexCacheInst,
32{
33    pub fn is_implemented(&self) -> bool {
34        match self {
35            RegexCache::Absent => false,
36            RegexCache::Implemented(_) => true,
37        }
38    }
39    pub fn get_instance(&self) -> Result<&T, RegexCacheError> {
40        match self {
41            RegexCache::Absent => Err(RegexCacheError::new("the instance is absent".to_owned())),
42            RegexCache::Implemented(inst) => Ok(inst),
43        }
44    }
45
46    pub fn instance(instance: T) -> Self {
47        RegexCache::Implemented(instance)
48    }
49}
50#[allow(clippy::derivable_impls)]
51impl Default for RegexCache {
52    fn default() -> Self {
53        RegexCache::Absent
54    }
55}
56
57/// A trait that defines the behavior for regex cache
58pub trait RegexCacheInst {
59    fn validate(&self, regex: &str, values: Vec<&Value>) -> Result<bool, RegexCacheError>;
60}
61
62/// Default implementation for regex cache. It uses Arc and Mutex to be capable of working
63/// among the threads.
64#[derive(Default, Debug, Clone)]
65pub struct DefaultRegexCacheInst {
66    cache: Arc<Mutex<HashMap<String, Regex>>>,
67}
68
69impl RegexCacheInst for DefaultRegexCacheInst {
70    fn validate(&self, regex: &str, values: Vec<&Value>) -> Result<bool, RegexCacheError> {
71        let mut cache = self.cache.lock()?;
72        if cache.contains_key(regex) {
73            let r = cache.get(regex).unwrap();
74            Ok(validate(r, values))
75        } else {
76            let new_reg = Regex::new(regex)?;
77            let result = validate(&new_reg, values);
78            cache.insert(regex.to_owned(), new_reg);
79            Ok(result)
80        }
81    }
82}
83
84fn validate(r: &Regex, values: Vec<&Value>) -> bool {
85    for el in values.iter() {
86        if let Some(v) = el.as_str() {
87            if r.is_match(v) {
88                return true;
89            }
90        }
91    }
92    false
93}
94
95pub struct RegexCacheError {
96    pub reason: String,
97}
98
99impl From<Error> for RegexCacheError {
100    fn from(value: Error) -> Self {
101        RegexCacheError::new(value.to_string())
102    }
103}
104
105impl<T> From<PoisonError<T>> for RegexCacheError {
106    fn from(value: PoisonError<T>) -> Self {
107        RegexCacheError::new(value.to_string())
108    }
109}
110
111impl RegexCacheError {
112    pub fn new(reason: String) -> Self {
113        Self { reason }
114    }
115}