assay_adapter_api/
shape.rs1use serde_json::Value;
2
3use crate::{AdapterError, AdapterErrorKind, AdapterResult};
4
5pub fn validate_json_shape(
7 value: &Value,
8 max_json_depth: Option<u64>,
9 max_array_length: Option<u64>,
10) -> AdapterResult<()> {
11 visit(value, 1, max_json_depth, max_array_length)
12}
13
14fn visit(
15 value: &Value,
16 depth: u64,
17 max_json_depth: Option<u64>,
18 max_array_length: Option<u64>,
19) -> AdapterResult<()> {
20 if let Some(limit) = max_json_depth {
21 if depth > limit {
22 return Err(AdapterError::new(
23 AdapterErrorKind::Measurement,
24 format!("payload exceeds max_json_depth ({limit})"),
25 ));
26 }
27 }
28
29 match value {
30 Value::Array(values) => {
31 if let Some(limit) = max_array_length {
32 if values.len() as u64 > limit {
33 return Err(AdapterError::new(
34 AdapterErrorKind::Measurement,
35 format!("payload exceeds max_array_length ({limit})"),
36 ));
37 }
38 }
39 for item in values {
40 visit(item, depth + 1, max_json_depth, max_array_length)?;
41 }
42 }
43 Value::Object(map) => {
44 for item in map.values() {
45 visit(item, depth + 1, max_json_depth, max_array_length)?;
46 }
47 }
48 _ => {}
49 }
50
51 Ok(())
52}
53
54#[cfg(test)]
55mod tests {
56 use serde_json::json;
57
58 use super::validate_json_shape;
59 use crate::AdapterErrorKind;
60
61 #[test]
62 fn json_shape_rejects_excessive_depth() {
63 let payload = json!({
64 "a": {
65 "b": {
66 "c": "too-deep"
67 }
68 }
69 });
70
71 let err = validate_json_shape(&payload, Some(3), None).unwrap_err();
72 assert_eq!(err.kind, AdapterErrorKind::Measurement);
73 assert!(err.message.contains("max_json_depth"));
74 }
75
76 #[test]
77 fn json_shape_rejects_excessive_array_length() {
78 let payload = json!({
79 "items": [1, 2, 3]
80 });
81
82 let err = validate_json_shape(&payload, None, Some(2)).unwrap_err();
83 assert_eq!(err.kind, AdapterErrorKind::Measurement);
84 assert!(err.message.contains("max_array_length"));
85 }
86}