1use cel::objects::Value;
7use cel::{Context, ResolveResult};
8use std::sync::Arc;
9
10pub fn register(ctx: &mut Context<'_>) {
12 ctx.add_function("jsonpatch.escapeKey", escape_key);
13}
14
15fn escape_key(s: Arc<String>) -> ResolveResult {
20 let escaped = s.replace('~', "~0").replace('/', "~1");
21 Ok(Value::String(Arc::new(escaped)))
22}
23
24#[cfg(test)]
25mod tests {
26 use super::*;
27 use cel::Program;
28
29 fn eval(expr: &str) -> Value {
30 let mut ctx = Context::default();
31 register(&mut ctx);
32 Program::compile(expr).unwrap().execute(&ctx).unwrap()
33 }
34
35 fn eval_str(expr: &str) -> String {
36 match eval(expr) {
37 Value::String(s) => (*s).clone(),
38 other => panic!("expected string, got {other:?}"),
39 }
40 }
41
42 #[test]
43 fn test_escape_tilde_and_slash() {
44 assert_eq!(
45 eval_str("jsonpatch.escapeKey('k8s.io/my~label')"),
46 "k8s.io~1my~0label"
47 );
48 }
49
50 #[test]
51 fn test_escape_tilde_only() {
52 assert_eq!(eval_str("jsonpatch.escapeKey('a~b')"), "a~0b");
53 }
54
55 #[test]
56 fn test_escape_slash_only() {
57 assert_eq!(eval_str("jsonpatch.escapeKey('a/b')"), "a~1b");
58 }
59
60 #[test]
61 fn test_escape_no_special_chars() {
62 assert_eq!(eval_str("jsonpatch.escapeKey('hello')"), "hello");
63 }
64
65 #[test]
66 fn test_escape_empty_string() {
67 assert_eq!(eval_str("jsonpatch.escapeKey('')"), "");
68 }
69
70 #[test]
71 fn test_escape_multiple() {
72 assert_eq!(eval_str("jsonpatch.escapeKey('~/~/')"), "~0~1~0~1");
73 }
74
75 #[test]
76 fn test_escape_order_matters() {
77 assert_eq!(eval_str("jsonpatch.escapeKey('~1')"), "~01");
80 }
81}