use async_trait::async_trait;
use serde_json::Value;
use crate::error::Result;
#[async_trait]
pub trait CacheBackend: Send + Sync {
async fn get(&self, key: &str) -> Result<Option<Value>>;
async fn set(&self, key: &str, value: Value, ttl_secs: Option<u64>) -> Result<()>;
async fn delete(&self, key: &str) -> Result<()>;
async fn clear(&self, pattern: Option<&str>) -> Result<usize>;
async fn exists(&self, key: &str) -> Result<bool>;
async fn close(&self) -> Result<()> {
Ok(())
}
}
pub(crate) fn compile_glob(pattern: &str) -> regex::Regex {
let mut out = String::from("^");
for ch in pattern.chars() {
match ch {
'*' => out.push_str(".*"),
'?' => out.push('.'),
c if c.is_ascii_alphanumeric() => out.push(c),
c => {
out.push('\\');
out.push(c);
}
}
}
out.push('$');
regex::Regex::new(&out).unwrap_or_else(|_| regex::Regex::new(".*").expect("trivial regex"))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn glob_matches_star() {
let re = compile_glob("user:*");
assert!(re.is_match("user:123"));
assert!(re.is_match("user:"));
assert!(!re.is_match("product:1"));
}
#[test]
fn glob_matches_question() {
let re = compile_glob("a?c");
assert!(re.is_match("abc"));
assert!(re.is_match("axc"));
assert!(!re.is_match("abbc"));
}
#[test]
fn glob_escapes_regex_metachars() {
let re = compile_glob("foo.bar");
assert!(re.is_match("foo.bar"));
assert!(!re.is_match("fooxbar"));
}
}