Skip to main content

assay_adapter_api/
shape.rs

1use serde_json::Value;
2
3use crate::{AdapterError, AdapterErrorKind, AdapterResult};
4
5/// Enforce shared JSON shape limits for adapter inputs.
6pub 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}