diff --git a/crates/burrego/src/opa/builtins/glob.rs b/crates/burrego/src/opa/builtins/glob.rs new file mode 100644 index 00000000..0842659b --- /dev/null +++ b/crates/burrego/src/opa/builtins/glob.rs @@ -0,0 +1,46 @@ +use anyhow::{anyhow, Result}; + +pub fn quote_meta(args: &[serde_json::Value]) -> Result { + if args.len() != 1 { + return Err(anyhow!("glob.quote_meta: wrong number of arguments")); + } + + let input = args[0] + .as_str() + .ok_or_else(|| anyhow!("glob.quote_meta: 1st parameter is not a string"))?; + + serde_json::to_value(escape(input)) + .map_err(|e| anyhow!("glob.quote_meta: cannot convert value into JSON: {:?}", e)) +} + +fn escape(s: &str) -> String { + let mut escaped = String::new(); + for c in s.chars() { + match c { + '*' | '?' | '\\' | '[' | ']' | '{' | '}' => { + escaped.push('\\'); + escaped.push(c); + } + c => { + escaped.push(c); + } + } + } + escaped +} + +#[cfg(test)] +mod test { + #[test] + fn escape() { + assert_eq!(super::escape("*.domain.com"), r"\*.domain.com",); + + assert_eq!(super::escape("*.domain-*.com"), r"\*.domain-\*.com",); + + assert_eq!(super::escape("domain.com"), r"domain.com",); + + assert_eq!(super::escape("domain-[ab].com"), r"domain-\[ab\].com",); + + assert_eq!(super::escape("nie?ce"), r"nie\?ce",); + } +} diff --git a/crates/burrego/src/opa/builtins/mod.rs b/crates/burrego/src/opa/builtins/mod.rs index c5abcfab..cce15acc 100644 --- a/crates/burrego/src/opa/builtins/mod.rs +++ b/crates/burrego/src/opa/builtins/mod.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; pub mod debugging; pub mod encoding; +pub mod glob; pub mod regex; pub mod semver; pub mod strings; @@ -31,6 +32,9 @@ pub fn get_builtins() -> BuiltinFunctionsMap { functions.insert("hex.encode", encoding::hex::encode); functions.insert("hex.decode", encoding::hex::decode); + // glob + functions.insert("glob.quote_meta", glob::quote_meta); + // regex functions.insert("regex.split", regex::split); functions.insert("regex.template_match", regex::template_match);