fakecloud_stepfunctions/
io_processing.rs1use serde_json::Value;
2
3pub fn apply_input_path(input: &Value, path: Option<&str>) -> Value {
8 match path {
9 None | Some("$") => input.clone(),
10 Some(p) => resolve_path(input, p),
11 }
12}
13
14pub fn apply_output_path(output: &Value, path: Option<&str>) -> Value {
17 match path {
18 None | Some("$") => output.clone(),
19 Some(p) => resolve_path(output, p),
20 }
21}
22
23pub fn apply_result_path(input: &Value, result: &Value, path: Option<&str>) -> Value {
28 match path {
29 None | Some("$") => result.clone(),
30 Some("null") => input.clone(),
31 Some(p) => set_at_path(input, p, result),
32 }
33}
34
35pub fn resolve_path(root: &Value, path: &str) -> Value {
38 if path == "$" {
39 return root.clone();
40 }
41
42 let path = path.strip_prefix("$.").unwrap_or(path);
43 let mut current = root;
44
45 for segment in split_path_segments(path) {
46 match segment {
47 PathSegment::Field(name) => {
48 current = match current.get(name) {
49 Some(v) => v,
50 None => return Value::Null,
51 };
52 }
53 PathSegment::Index(name, idx) => {
54 current = match current.get(name) {
55 Some(v) => match v.get(idx) {
56 Some(v) => v,
57 None => return Value::Null,
58 },
59 None => return Value::Null,
60 };
61 }
62 }
63 }
64
65 current.clone()
66}
67
68fn set_at_path(root: &Value, path: &str, value: &Value) -> Value {
70 let mut result = root.clone();
71 let path = path.strip_prefix("$.").unwrap_or(path);
72 let segments: Vec<&str> = path.split('.').collect();
73
74 let mut current = &mut result;
75 for (i, segment) in segments.iter().enumerate() {
76 if i == segments.len() - 1 {
77 if let Some(obj) = current.as_object_mut() {
79 obj.insert(segment.to_string(), value.clone());
80 }
81 } else {
82 if current.get(*segment).is_none() {
84 if let Some(obj) = current.as_object_mut() {
85 obj.insert(segment.to_string(), serde_json::json!({}));
86 }
87 }
88 current = current.get_mut(*segment).unwrap();
89 }
90 }
91
92 result
93}
94
95enum PathSegment<'a> {
96 Field(&'a str),
97 Index(&'a str, usize),
98}
99
100fn split_path_segments(path: &str) -> Vec<PathSegment<'_>> {
101 let mut segments = Vec::new();
102 for part in path.split('.') {
103 if let Some(bracket_pos) = part.find('[') {
104 let name = &part[..bracket_pos];
105 let idx_str = &part[bracket_pos + 1..part.len() - 1];
106 if let Ok(idx) = idx_str.parse::<usize>() {
107 segments.push(PathSegment::Index(name, idx));
108 } else {
109 segments.push(PathSegment::Field(part));
110 }
111 } else {
112 segments.push(PathSegment::Field(part));
113 }
114 }
115 segments
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121 use serde_json::json;
122
123 #[test]
124 fn test_resolve_path_root() {
125 let input = json!({"a": 1});
126 assert_eq!(resolve_path(&input, "$"), input);
127 }
128
129 #[test]
130 fn test_resolve_path_simple_field() {
131 let input = json!({"name": "hello", "value": 42});
132 assert_eq!(resolve_path(&input, "$.name"), json!("hello"));
133 assert_eq!(resolve_path(&input, "$.value"), json!(42));
134 }
135
136 #[test]
137 fn test_resolve_path_nested() {
138 let input = json!({"a": {"b": {"c": 99}}});
139 assert_eq!(resolve_path(&input, "$.a.b.c"), json!(99));
140 }
141
142 #[test]
143 fn test_resolve_path_missing() {
144 let input = json!({"a": 1});
145 assert_eq!(resolve_path(&input, "$.missing"), Value::Null);
146 }
147
148 #[test]
149 fn test_resolve_path_array_index() {
150 let input = json!({"items": [10, 20, 30]});
151 assert_eq!(resolve_path(&input, "$.items[0]"), json!(10));
152 assert_eq!(resolve_path(&input, "$.items[2]"), json!(30));
153 }
154
155 #[test]
156 fn test_apply_input_path_default() {
157 let input = json!({"x": 1});
158 assert_eq!(apply_input_path(&input, None), input);
159 assert_eq!(apply_input_path(&input, Some("$")), input);
160 }
161
162 #[test]
163 fn test_apply_result_path_default() {
164 let input = json!({"x": 1});
165 let result = json!({"y": 2});
166 assert_eq!(apply_result_path(&input, &result, None), result);
168 assert_eq!(apply_result_path(&input, &result, Some("$")), result);
169 }
170
171 #[test]
172 fn test_apply_result_path_null() {
173 let input = json!({"x": 1});
174 let result = json!({"y": 2});
175 assert_eq!(apply_result_path(&input, &result, Some("null")), input);
177 }
178
179 #[test]
180 fn test_apply_result_path_nested() {
181 let input = json!({"x": 1});
182 let result = json!("hello");
183 let output = apply_result_path(&input, &result, Some("$.result"));
184 assert_eq!(output, json!({"x": 1, "result": "hello"}));
185 }
186
187 #[test]
188 fn test_apply_output_path() {
189 let output = json!({"a": 1, "b": 2});
190 assert_eq!(apply_output_path(&output, Some("$.a")), json!(1));
191 assert_eq!(apply_output_path(&output, None), output);
192 }
193}