1#![doc = include_str!("../README.md")]
2
3extern crate alloc;
4
5use alloc::collections::BTreeMap;
6
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct Catalog {
13 pub version: u32,
14 #[serde(default, skip_serializing_if = "Option::is_none")]
15 pub title: Option<String>,
16 pub schemas: Vec<SchemaEntry>,
17 #[serde(default, skip_serializing_if = "Vec::is_empty")]
18 pub groups: Vec<CatalogGroup>,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct CatalogGroup {
27 pub name: String,
28 pub description: String,
29 pub schemas: Vec<String>,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct SchemaEntry {
36 pub name: String,
37 pub description: String,
38 pub url: String,
39 #[serde(default, rename = "sourceUrl", skip_serializing_if = "Option::is_none")]
40 pub source_url: Option<String>,
41 #[serde(default, rename = "fileMatch", skip_serializing_if = "Vec::is_empty")]
42 pub file_match: Vec<String>,
43 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
44 pub versions: BTreeMap<String, String>,
45}
46
47pub fn parse_catalog(json: &str) -> Result<Catalog, serde_json::Error> {
53 serde_json::from_str(json)
54}
55
56pub fn parse_catalog_value(value: serde_json::Value) -> Result<Catalog, serde_json::Error> {
62 serde_json::from_value(value)
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn round_trip_catalog() {
71 let catalog = Catalog {
72 version: 1,
73 title: None,
74 schemas: vec![SchemaEntry {
75 name: "Test Schema".into(),
76 description: "A test schema".into(),
77 url: "https://example.com/test.json".into(),
78 source_url: None,
79 file_match: vec!["*.test.json".into()],
80 versions: BTreeMap::new(),
81 }],
82 groups: vec![],
83 };
84 let json = serde_json::to_string_pretty(&catalog).expect("serialize");
85 let parsed: Catalog = serde_json::from_str(&json).expect("deserialize");
86 assert_eq!(parsed.version, 1);
87 assert_eq!(parsed.schemas.len(), 1);
88 assert_eq!(parsed.schemas[0].name, "Test Schema");
89 assert_eq!(parsed.schemas[0].file_match, vec!["*.test.json"]);
90 }
91
92 #[test]
93 fn parse_catalog_from_json_string() {
94 let json = r#"{"version":1,"schemas":[{"name":"test","description":"desc","url":"https://example.com/s.json","fileMatch":["*.json"]}]}"#;
95 let catalog = parse_catalog(json).expect("parse");
96 assert_eq!(catalog.schemas.len(), 1);
97 assert_eq!(catalog.schemas[0].name, "test");
98 assert_eq!(catalog.schemas[0].file_match, vec!["*.json"]);
99 }
100
101 #[test]
102 fn empty_file_match_omitted_in_serialization() {
103 let entry = SchemaEntry {
104 name: "No Match".into(),
105 description: "desc".into(),
106 url: "https://example.com/no.json".into(),
107 source_url: None,
108 file_match: vec![],
109 versions: BTreeMap::new(),
110 };
111 let json = serde_json::to_string(&entry).expect("serialize");
112 assert!(!json.contains("fileMatch"));
113 assert!(!json.contains("sourceUrl"));
114 assert!(!json.contains("versions"));
115 }
116
117 #[test]
118 fn source_url_serialized_as_camel_case() {
119 let entry = SchemaEntry {
120 name: "Test".into(),
121 description: "desc".into(),
122 url: "https://catalog.example.com/test.json".into(),
123 source_url: Some("https://upstream.example.com/test.json".into()),
124 file_match: vec![],
125 versions: BTreeMap::new(),
126 };
127 let json = serde_json::to_string(&entry).expect("serialize");
128 assert!(json.contains("\"sourceUrl\""));
129 assert!(json.contains("https://upstream.example.com/test.json"));
130
131 let parsed: SchemaEntry = serde_json::from_str(&json).expect("deserialize");
133 assert_eq!(
134 parsed.source_url.as_deref(),
135 Some("https://upstream.example.com/test.json")
136 );
137 }
138
139 #[test]
140 fn deserialize_with_versions() {
141 let json = r#"{
142 "version": 1,
143 "schemas": [{
144 "name": "test",
145 "description": "desc",
146 "url": "https://example.com/s.json",
147 "versions": {"draft-07": "https://example.com/draft07.json"}
148 }]
149 }"#;
150 let catalog = parse_catalog(json).expect("parse");
151 assert_eq!(
152 catalog.schemas[0].versions.get("draft-07"),
153 Some(&"https://example.com/draft07.json".to_string())
154 );
155 }
156}