1use crate::evaluation::evaluate_data::{
4 attribute_evaluate_data_error, evaluate_data_from_entries, evaluate_data_map_from_entries,
5 evaluate_data_null_list_item, EvaluateDataValue,
6};
7use crate::Error;
8use indexmap::IndexMap;
9use serde_json::Value;
10
11pub fn evaluate_data_value_from_json_value(
16 value: Value,
17) -> Result<Option<EvaluateDataValue>, Error> {
18 let mut frames: Vec<JsonFrame> = Vec::new();
19 let mut step = JsonStep::Descend(value);
20 loop {
21 step = match step {
22 JsonStep::Descend(value) => match value {
23 Value::Null => JsonStep::Deliver(None),
24 Value::String(s) => JsonStep::Deliver(Some(EvaluateDataValue::Literal(s))),
25 Value::Bool(b) => JsonStep::Deliver(Some(EvaluateDataValue::Boolean(b))),
26 Value::Number(n) => {
27 return Err(Error::request(
28 format!("pass as string: \"{n}\""),
29 Some("JSON numbers have no exact native type"),
30 ))
31 }
32 Value::Object(obj) => {
33 frames.push(JsonFrame::Object {
34 entries: Vec::with_capacity(obj.len()),
35 children: obj.into_iter(),
36 pending_key: None,
37 });
38 JsonStep::NextChild
39 }
40 Value::Array(items) => {
41 frames.push(JsonFrame::Array {
42 list: Vec::with_capacity(items.len()),
43 children: items.into_iter(),
44 });
45 JsonStep::NextChild
46 }
47 },
48 JsonStep::NextChild => {
49 let frame = frames
50 .last_mut()
51 .expect("BUG: json frame stack empty while reading a child");
52 match frame.next_child() {
53 Some(child) => JsonStep::Descend(child),
54 None => {
55 let frame = frames
56 .pop()
57 .expect("BUG: json frame stack empty on completion");
58 JsonStep::Deliver(Some(frame.finish()?))
59 }
60 }
61 }
62 JsonStep::Deliver(item) => match frames.last_mut() {
63 None => return Ok(item),
64 Some(JsonFrame::Object {
65 entries,
66 pending_key,
67 ..
68 }) => {
69 if let Some(parsed) = item {
70 let key = pending_key
71 .take()
72 .expect("BUG: json object child delivered without its key");
73 entries.push((key, parsed));
74 }
75 JsonStep::NextChild
76 }
77 Some(JsonFrame::Array { list, .. }) => {
78 let parsed =
79 item.ok_or_else(|| evaluate_data_null_list_item(list.len() + 1))?;
80 list.push(parsed);
81 JsonStep::NextChild
82 }
83 },
84 };
85 }
86}
87
88enum JsonStep {
89 Descend(Value),
90 NextChild,
91 Deliver(Option<EvaluateDataValue>),
92}
93
94enum JsonFrame {
95 Object {
96 entries: Vec<(String, EvaluateDataValue)>,
97 children: serde_json::map::IntoIter,
98 pending_key: Option<String>,
99 },
100 Array {
101 list: Vec<EvaluateDataValue>,
102 children: std::vec::IntoIter<Value>,
103 },
104}
105
106impl JsonFrame {
107 fn next_child(&mut self) -> Option<Value> {
108 match self {
109 JsonFrame::Object {
110 children,
111 pending_key,
112 ..
113 } => children.next().map(|(key, child)| {
114 *pending_key = Some(key);
115 child
116 }),
117 JsonFrame::Array { children, .. } => children.next(),
118 }
119 }
120
121 fn finish(self) -> Result<EvaluateDataValue, Error> {
122 match self {
123 JsonFrame::Object { entries, .. } => evaluate_data_map_from_entries(entries),
124 JsonFrame::Array { list, .. } => Ok(EvaluateDataValue::List(list)),
125 }
126 }
127}
128
129pub fn parse_evaluate_data_object(
132 data: &Option<Value>,
133) -> Result<IndexMap<String, EvaluateDataValue>, Error> {
134 match data {
135 None | Some(Value::Null) => Ok(IndexMap::new()),
136 Some(Value::Object(map)) => {
137 let mut entries = Vec::with_capacity(map.len());
138 for (key, value) in map {
139 let parsed = evaluate_data_value_from_json_value(value.clone())
140 .map_err(|e| attribute_evaluate_data_error(e, key))?;
141 if let Some(parsed) = parsed {
142 entries.push((key.clone(), parsed));
143 }
144 }
145 evaluate_data_from_entries(entries)
146 }
147 Some(_) => Err(Error::request("data must be a JSON object", None::<String>)),
148 }
149}
150
151pub fn resolve_evaluate_rules(rules: &Option<Value>) -> Result<Option<Vec<String>>, Error> {
153 match rules {
154 None | Some(Value::Null) => Ok(None),
155 Some(Value::String(s)) => {
156 let name = s.trim();
157 if name.is_empty() {
158 return Err(Error::request("rules must not be empty", None::<String>));
159 }
160 Ok(Some(vec![name.to_string()]))
161 }
162 Some(Value::Array(arr)) => {
163 if arr.is_empty() {
164 return Err(Error::request("rules must not be empty", None::<String>));
165 }
166 let mut names = Vec::with_capacity(arr.len());
167 for (i, v) in arr.iter().enumerate() {
168 match v {
169 Value::String(s) => {
170 let name = s.trim();
171 if name.is_empty() {
172 return Err(Error::request(
173 format!("rules[{i}] must not be empty"),
174 None::<String>,
175 ));
176 }
177 names.push(name.to_string());
178 }
179 _ => {
180 return Err(Error::request(
181 format!("rules[{i}] must be a string"),
182 None::<String>,
183 ));
184 }
185 }
186 }
187 Ok(Some(names))
188 }
189 Some(_) => Err(Error::request(
190 "rules must be a string or an array of strings",
191 None::<String>,
192 )),
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199
200 #[test]
201 fn json_null_is_omitted() {
202 assert_eq!(
203 evaluate_data_value_from_json_value(Value::Null).expect("null is omit"),
204 None
205 );
206 }
207
208 #[test]
209 fn json_nesting_beyond_serde_limit_fails_to_parse() {
210 let text = format!("{}1{}", "[".repeat(1000), "]".repeat(1000));
211 assert!(serde_json::from_str::<Value>(&text).is_err());
212 }
213
214 #[test]
215 fn json_number_is_request_error() {
216 let err = evaluate_data_value_from_json_value(serde_json::json!(84)).expect_err("number");
217 assert!(err.message().contains("pass as string: \"84\""), "{err}");
218 }
219
220 #[test]
221 fn json_number_error_names_top_level_key() {
222 let err = parse_evaluate_data_object(&Some(serde_json::json!({
223 "price": { "eur": 84 }
224 })))
225 .expect_err("nested number");
226 assert!(err.message().contains("pass as string: \"84\""), "{err}");
227 assert_eq!(err.related_data(), Some("price"));
228 }
229
230 #[test]
231 fn parse_evaluate_data_object_null_field_is_omitted() {
232 let only_null = Some(serde_json::json!({ "x": null }));
233 let empty = parse_evaluate_data_object(&only_null).expect("null field omits");
234 assert!(empty.is_empty(), "got: {empty:?}");
235
236 let mixed = Some(serde_json::json!({ "x": null, "y": "1" }));
237 let map = parse_evaluate_data_object(&mixed).expect("null field omits");
238 assert_eq!(map.len(), 1);
239 assert_eq!(
240 map.get("y"),
241 Some(&EvaluateDataValue::Literal("1".to_string()))
242 );
243 assert!(!map.contains_key("x"));
244 }
245
246 #[test]
247 fn null_list_item_is_request_error_naming_position_and_key() {
248 let err = parse_evaluate_data_object(&Some(serde_json::json!({
249 "fares": ["89 eur", null, "120 eur"]
250 })))
251 .expect_err("null list item");
252 assert_eq!(err.message(), "list item 2 is null");
253 assert_eq!(err.related_data(), Some("fares"));
254 }
255
256 #[test]
257 fn null_nested_list_item_is_request_error() {
258 let err = parse_evaluate_data_object(&Some(serde_json::json!({
259 "components": [{ "own_cost": "5 eur", "components": [null] }]
260 })))
261 .expect_err("nested null list item");
262 assert_eq!(err.message(), "list item 1 is null");
263 assert_eq!(err.related_data(), Some("components"));
264 }
265
266 #[test]
267 fn null_field_inside_list_row_is_omitted() {
268 let parsed = parse_evaluate_data_object(&Some(serde_json::json!({
269 "staff": [{ "name": "Ada", "salary": null }]
270 })))
271 .expect("null row field omits");
272 let Some(EvaluateDataValue::List(rows)) = parsed.get("staff") else {
273 panic!("expected list, got {parsed:?}");
274 };
275 assert_eq!(rows.len(), 1);
276 let EvaluateDataValue::Map(row) = &rows[0] else {
277 panic!("expected row map, got {:?}", rows[0]);
278 };
279 assert_eq!(row.len(), 1);
280 assert_eq!(
281 row.get("name"),
282 Some(&EvaluateDataValue::Literal("Ada".to_string()))
283 );
284 }
285
286 #[test]
287 fn parse_evaluate_data_object_unit_map_and_array() {
288 let parsed = parse_evaluate_data_object(&Some(serde_json::json!({
289 "price": { "eur": "84" },
290 "flags": [true, "x"]
291 })))
292 .expect("object");
293 match parsed.get("price") {
294 Some(EvaluateDataValue::Map(m)) => {
295 assert_eq!(
296 m.get("eur"),
297 Some(&EvaluateDataValue::Literal("84".to_string()))
298 );
299 }
300 other => panic!("expected map, got {other:?}"),
301 }
302 match parsed.get("flags") {
303 Some(EvaluateDataValue::List(items)) => {
304 assert_eq!(items[0], EvaluateDataValue::Boolean(true));
305 assert_eq!(items[1], EvaluateDataValue::Literal("x".to_string()));
306 }
307 other => panic!("expected list, got {other:?}"),
308 }
309 }
310
311 #[test]
312 fn duplicate_case_keys_error() {
313 let err = parse_evaluate_data_object(&Some(serde_json::json!({
314 "Qty": "1",
315 "qty": "2"
316 })))
317 .expect_err("dup");
318 assert!(err.message().contains("Duplicate data key"), "{err}");
319 }
320
321 #[test]
322 fn duplicate_case_keys_in_nested_object_error() {
323 let err = parse_evaluate_data_object(&Some(serde_json::json!({
324 "price": { "EUR": "1", "eur": "2" }
325 })))
326 .expect_err("dup");
327 assert!(
328 err.message().contains("Duplicate object key 'eur'"),
329 "{err}"
330 );
331 assert_eq!(err.related_data(), Some("price"));
332 }
333}