use cel::{Context, ResolveResult, objects::Value};
use std::sync::Arc;
pub fn register(ctx: &mut Context<'_>) {
ctx.add_function("jsonpatch.escapeKey", escape_key);
}
fn escape_key(s: Arc<String>) -> ResolveResult {
let escaped = s.replace('~', "~0").replace('/', "~1");
Ok(Value::String(Arc::new(escaped)))
}
#[cfg(test)]
mod tests {
use super::*;
use cel::Program;
fn eval(expr: &str) -> Value {
let mut ctx = Context::default();
register(&mut ctx);
Program::compile(expr).unwrap().execute(&ctx).unwrap()
}
fn eval_str(expr: &str) -> String {
match eval(expr) {
Value::String(s) => (*s).clone(),
other => panic!("expected string, got {other:?}"),
}
}
#[test]
fn test_escape_tilde_and_slash() {
assert_eq!(
eval_str("jsonpatch.escapeKey('k8s.io/my~label')"),
"k8s.io~1my~0label"
);
}
#[test]
fn test_escape_tilde_only() {
assert_eq!(eval_str("jsonpatch.escapeKey('a~b')"), "a~0b");
}
#[test]
fn test_escape_slash_only() {
assert_eq!(eval_str("jsonpatch.escapeKey('a/b')"), "a~1b");
}
#[test]
fn test_escape_no_special_chars() {
assert_eq!(eval_str("jsonpatch.escapeKey('hello')"), "hello");
}
#[test]
fn test_escape_empty_string() {
assert_eq!(eval_str("jsonpatch.escapeKey('')"), "");
}
#[test]
fn test_escape_multiple() {
assert_eq!(eval_str("jsonpatch.escapeKey('~/~/')"), "~0~1~0~1");
}
#[test]
fn test_escape_order_matters() {
assert_eq!(eval_str("jsonpatch.escapeKey('~1')"), "~01");
}
}