• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

input-output-hk / catalyst-libs / 17068411200

19 Aug 2025 11:39AM UTC coverage: 66.968% (+0.06%) from 66.912%
17068411200

push

github

web-flow
feat(rust/signed-doc): Add support for jsonschema draft 2020-12 (#493)

* feat: jsonschema 2020-12

* feat: dynamic version

* feat: jsonschema struct

* chore: lintfix

* chore: fmtfix

* chore: lintfix

* fix: content schema wrapper

* fix: remove access

71 of 81 new or added lines in 3 files covered. (87.65%)

1 existing line in 1 file now uncovered.

12592 of 18803 relevant lines covered (66.97%)

3137.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.51
/rust/signed_doc/src/validator/json_schema.rs
1
//! A wrapper around a JSON Schema validator.
2

3
use std::ops::Deref;
4

5
use anyhow::anyhow;
6
use jsonschema::{options, Draft, Validator};
7
use serde_json::Value;
8

9
/// Wrapper around a JSON Schema validator.
10
///
11
/// Attempts to detect the draft version from the `$schema` field.
12
/// If not specified, it tries Draft2020-12 first, then falls back to Draft7.
13
/// Returns an error if schema is invalid for both.
14
pub(crate) struct JsonSchema(Validator);
15

16
impl Deref for JsonSchema {
17
    type Target = Validator;
18

19
    fn deref(&self) -> &Self::Target {
14✔
20
        &self.0
14✔
21
    }
14✔
22
}
23

24
impl TryFrom<&Value> for JsonSchema {
25
    type Error = anyhow::Error;
26

27
    fn try_from(schema: &Value) -> std::result::Result<Self, Self::Error> {
31✔
28
        let draft_version = if let Some(schema) = schema.get("$schema").and_then(|s| s.as_str()) {
31✔
29
            if schema.contains("draft-07") {
25✔
30
                Some(Draft::Draft7)
9✔
31
            } else if schema.contains("2020-12") {
16✔
32
                Some(Draft::Draft202012)
16✔
33
            } else {
NEW
34
                None
×
35
            }
36
        } else {
37
            None
6✔
38
        };
39

40
        if let Some(draft) = draft_version {
31✔
41
            let validator = options()
25✔
42
                .with_draft(draft)
25✔
43
                .build(schema)
25✔
44
                .map_err(|e| anyhow!("Invalid JSON Schema: {e}"))?;
25✔
45

46
            Ok(JsonSchema(validator))
24✔
47
        } else {
48
            // if draft not specified or not detectable:
49
            // try draft2020-12
50
            if let Ok(validator) = options().with_draft(Draft::Draft202012).build(schema) {
6✔
51
                return Ok(JsonSchema(validator));
6✔
NEW
52
            }
×
53

54
            // fallback to draft7
NEW
55
            if let Ok(validator) = options().with_draft(Draft::Draft7).build(schema) {
×
NEW
56
                return Ok(JsonSchema(validator));
×
NEW
57
            }
×
58

NEW
59
            Err(anyhow!(
×
NEW
60
                "Could not detect draft version and schema is not valid against Draft2020-12 or Draft7"
×
NEW
61
            ))
×
62
        }
63
    }
31✔
64
}
65

66
#[cfg(test)]
67
mod tests {
68
    use serde_json::json;
69

70
    use super::*;
71

72
    #[test]
73
    fn valid_draft7_schema() {
1✔
74
        let schema = json!({
1✔
75
            "$schema": "http://json-schema.org/draft-07/schema#",
1✔
76
            "type": "object",
1✔
77
            "properties": {
1✔
78
                "name": { "type": "string" }
1✔
79
            }
80
        });
81

82
        let result = JsonSchema::try_from(&schema);
1✔
83
        assert!(result.is_ok(), "Expected Draft7 schema to be valid");
1✔
84
    }
1✔
85

86
    #[test]
87
    fn valid_draft2020_12_schema() {
1✔
88
        let schema = json!({
1✔
89
            "$schema": "https://json-schema.org/draft/2020-12/schema",
1✔
90
            "type": "object",
1✔
91
            "properties": {
1✔
92
                "age": { "type": "integer" }
1✔
93
            }
94
        });
95

96
        let result = JsonSchema::try_from(&schema);
1✔
97
        assert!(result.is_ok(), "Expected Draft2020-12 schema to be valid");
1✔
98
    }
1✔
99

100
    #[test]
101
    fn schema_without_draft_should_fallback() {
1✔
102
        // Valid in both Draft2020-12 and Draft7
103
        let schema = json!({
1✔
104
            "type": "object",
1✔
105
            "properties": {
1✔
106
                "id": { "type": "number" }
1✔
107
            }
108
        });
109

110
        let result = JsonSchema::try_from(&schema);
1✔
111
        assert!(
1✔
112
            result.is_ok(),
1✔
NEW
113
            "Expected schema without $schema to fallback and succeed"
×
114
        );
115
    }
1✔
116

117
    #[test]
118
    fn invalid_schema_should_error() {
1✔
119
        // Invalid schema: "type" is not a valid keyword here
120
        let schema = json!({
1✔
121
            "$schema": "http://json-schema.org/draft-07/schema#",
1✔
122
            "type": "not-a-valid-type"
1✔
123
        });
124

125
        let result = JsonSchema::try_from(&schema);
1✔
126
        assert!(
1✔
127
            result.is_err(),
1✔
NEW
128
            "Expected invalid schema to return an error"
×
129
        );
130
    }
1✔
131

132
    #[test]
133
    fn empty_object_schema() {
1✔
134
        let schema = json!({});
1✔
135

136
        let result = JsonSchema::try_from(&schema);
1✔
137
        assert!(result.is_ok());
1✔
138
    }
1✔
139
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc