swf_core/models/
schema.rs1use crate::models::resource::*;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4
5fn default_schema_format() -> String {
7 SchemaFormat::JSON.to_string()
8}
9
10pub struct SchemaFormat;
12impl SchemaFormat {
13 pub const AVRO: &'static str = "avro";
15 pub const JSON: &'static str = "json";
17 pub const XML: &'static str = "xml";
19}
20
21#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
23pub struct SchemaDefinition {
24 #[serde(default = "default_schema_format")]
26 pub format: String,
27
28 #[serde(skip_serializing_if = "Option::is_none")]
30 pub resource: Option<ExternalResourceDefinition>,
31
32 #[serde(skip_serializing_if = "Option::is_none")]
34 pub document: Option<Value>,
35}
36
37impl Default for SchemaDefinition {
38 fn default() -> Self {
39 SchemaDefinition {
40 format: default_schema_format(),
41 resource: None,
42 document: None,
43 }
44 }
45}
46
47impl SchemaDefinition {
48 pub fn with_document(format: &str, document: Value) -> Self {
50 Self {
51 format: format.to_string(),
52 resource: None,
53 document: Some(document),
54 }
55 }
56
57 pub fn with_resource(format: &str, resource: ExternalResourceDefinition) -> Self {
59 Self {
60 format: format.to_string(),
61 resource: Some(resource),
62 document: None,
63 }
64 }
65
66 pub fn validate(&self) -> Result<(), SchemaValidationError> {
68 match (&self.document, &self.resource) {
69 (Some(_), Some(_)) => Err(SchemaValidationError::BothSet),
70 (None, None) => Err(SchemaValidationError::NeitherSet),
71 _ => Ok(()),
72 }
73 }
74
75 pub fn is_document(&self) -> bool {
77 self.document.is_some() && self.resource.is_none()
78 }
79
80 pub fn is_resource(&self) -> bool {
82 self.resource.is_some() && self.document.is_none()
83 }
84}
85
86#[derive(Debug, Clone, PartialEq, Eq)]
88pub enum SchemaValidationError {
89 BothSet,
91 NeitherSet,
93}
94
95impl std::fmt::Display for SchemaValidationError {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 match self {
98 SchemaValidationError::BothSet => write!(
99 f,
100 "Schema 'document' and 'resource' are mutually exclusive; only one can be set"
101 ),
102 SchemaValidationError::NeitherSet => {
103 write!(f, "Schema must have either 'document' or 'resource' set")
104 }
105 }
106 }
107}
108
109impl std::error::Error for SchemaValidationError {}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn test_schema_default_format() {
117 let schema = SchemaDefinition::default();
118 assert_eq!(schema.format, "json");
119 }
120
121 #[test]
122 fn test_schema_with_document() {
123 let doc = serde_json::json!({"type": "object", "properties": {"key": {"type": "string"}}});
124 let schema = SchemaDefinition::with_document("json", doc.clone());
125 assert!(schema.is_document());
126 assert!(!schema.is_resource());
127 assert_eq!(schema.document, Some(doc));
128 }
129
130 #[test]
131 fn test_schema_with_resource() {
132 let resource = ExternalResourceDefinition {
133 name: Some("mySchema".to_string()),
134 endpoint: crate::models::resource::OneOfEndpointDefinitionOrUri::Uri(
135 "http://example.com/schema".to_string(),
136 ),
137 };
138 let schema = SchemaDefinition::with_resource("json", resource);
139 assert!(schema.is_resource());
140 assert!(!schema.is_document());
141 }
142
143 #[test]
144 fn test_schema_validate_document() {
145 let schema = SchemaDefinition::with_document("json", serde_json::json!({}));
146 assert!(schema.validate().is_ok());
147 }
148
149 #[test]
150 fn test_schema_validate_neither() {
151 let schema = SchemaDefinition::default();
152 assert!(matches!(
153 schema.validate(),
154 Err(SchemaValidationError::NeitherSet)
155 ));
156 }
157
158 #[test]
159 fn test_schema_deserialize_with_document() {
160 let json = r#"{
161 "format": "json",
162 "document": {"type": "object"}
163 }"#;
164 let schema: SchemaDefinition = serde_json::from_str(json).unwrap();
165 assert_eq!(schema.format, "json");
166 assert!(schema.document.is_some());
167 }
168
169 #[test]
170 fn test_schema_deserialize_with_resource() {
171 let json = r#"{
172 "format": "avro",
173 "resource": {
174 "name": "myAvroSchema",
175 "endpoint": "http://example.com/schema.avsc"
176 }
177 }"#;
178 let schema: SchemaDefinition = serde_json::from_str(json).unwrap();
179 assert_eq!(schema.format, "avro");
180 assert!(schema.resource.is_some());
181 }
182
183 #[test]
184 fn test_schema_roundtrip() {
185 let json = r#"{"format": "json", "document": {"type": "object"}}"#;
186 let schema: SchemaDefinition = serde_json::from_str(json).unwrap();
187 let serialized = serde_json::to_string(&schema).unwrap();
188 let deserialized: SchemaDefinition = serde_json::from_str(&serialized).unwrap();
189 assert_eq!(schema, deserialized);
190 }
191
192 #[test]
195 fn test_schema_default_format_on_deserialize() {
196 let json = r#"{"document": {"type": "object"}}"#;
198 let schema: SchemaDefinition = serde_json::from_str(json).unwrap();
199 assert_eq!(schema.format, "json");
200 }
201
202 #[test]
203 fn test_schema_format_with_version() {
204 let json = r#"{"format": "json:2020-12", "document": {"type": "object"}}"#;
206 let schema: SchemaDefinition = serde_json::from_str(json).unwrap();
207 assert_eq!(schema.format, "json:2020-12");
208 }
209
210 #[test]
211 fn test_schema_avro_format() {
212 let json = r#"{"format": "avro", "document": {"type": "record", "name": "Test"}}"#;
213 let schema: SchemaDefinition = serde_json::from_str(json).unwrap();
214 assert_eq!(schema.format, "avro");
215 }
216
217 #[test]
218 fn test_schema_validate_both_set() {
219 let mut schema = SchemaDefinition::with_document("json", serde_json::json!({}));
221 schema.resource = Some(ExternalResourceDefinition {
222 name: Some("test".to_string()),
223 endpoint: crate::models::resource::OneOfEndpointDefinitionOrUri::Uri(
224 "http://example.com".to_string(),
225 ),
226 });
227 assert!(matches!(
228 schema.validate(),
229 Err(SchemaValidationError::BothSet)
230 ));
231 }
232
233 #[test]
234 fn test_schema_with_resource_roundtrip() {
235 let json = r#"{
236 "format": "json",
237 "resource": {
238 "name": "mySchema",
239 "endpoint": "http://example.com/schema.json"
240 }
241 }"#;
242 let schema: SchemaDefinition = serde_json::from_str(json).unwrap();
243 let serialized = serde_json::to_string(&schema).unwrap();
244 let deserialized: SchemaDefinition = serde_json::from_str(&serialized).unwrap();
245 assert_eq!(schema, deserialized);
246 }
247
248 #[test]
249 fn test_schema_validation_error_display() {
250 let both = SchemaValidationError::BothSet;
251 assert!(both.to_string().contains("mutually exclusive"));
252
253 let neither = SchemaValidationError::NeitherSet;
254 assert!(neither.to_string().contains("must have either"));
255 }
256}