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