1use crate::types::{MoldError, Schema, SchemaType};
2use crate::utils::{get_file_stem, to_pascal_case};
3use anyhow::Result;
4use std::path::Path;
5
6use super::inference::{infer_type_flat, infer_type_with_extraction};
7
8pub fn parse_json_file(path: &Path, name: Option<&str>, flat_mode: bool) -> Result<Schema> {
9 let content = std::fs::read_to_string(path)?;
10 let root_name = name
11 .map(to_pascal_case)
12 .unwrap_or_else(|| to_pascal_case(get_file_stem(path).as_str()));
13
14 parse_json_string(&content, &root_name, flat_mode)
15}
16
17pub fn parse_json_string(json: &str, name: &str, flat_mode: bool) -> Result<Schema> {
18 let value: serde_json::Value = serde_json::from_str(json)?;
19 parse_json_value(&value, name, flat_mode)
20}
21
22pub fn parse_json_value(value: &serde_json::Value, name: &str, flat_mode: bool) -> Result<Schema> {
23 let mut nested_types = Vec::new();
24 let mut path = vec![name.to_string()];
25
26 let root_type = if flat_mode {
27 infer_type_flat(value)
28 } else {
29 infer_type_with_extraction(value, &mut path, &mut nested_types)
30 };
31
32 if !matches!(root_type, SchemaType::Object(_)) {
33 return Err(MoldError::InvalidRoot(format!("{:?}", value)).into());
34 }
35
36 Ok(Schema::new(name, root_type).with_nested_types(nested_types))
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42
43 #[test]
44 fn test_parse_simple_object() {
45 let json = r#"{"name": "John", "age": 30}"#;
46 let schema = parse_json_string(json, "User", true).unwrap();
47
48 assert_eq!(schema.name, "User");
49 if let SchemaType::Object(obj) = &schema.root_type {
50 assert_eq!(obj.fields.len(), 2);
51 } else {
52 panic!("Expected Object type");
53 }
54 }
55
56 #[test]
57 fn test_infer_integer_vs_number() {
58 let int_json = r#"{"count": 42}"#;
59 let float_json = r#"{"price": 19.99}"#;
60
61 let int_schema = parse_json_string(int_json, "Test", true).unwrap();
62 let float_schema = parse_json_string(float_json, "Test", true).unwrap();
63
64 if let SchemaType::Object(obj) = &int_schema.root_type {
65 assert_eq!(obj.fields[0].field_type, SchemaType::Integer);
66 }
67
68 if let SchemaType::Object(obj) = &float_schema.root_type {
69 assert_eq!(obj.fields[0].field_type, SchemaType::Number);
70 }
71 }
72
73 #[test]
74 fn test_parse_array() {
75 let json = r#"{"tags": ["a", "b", "c"]}"#;
76 let schema = parse_json_string(json, "Test", true).unwrap();
77
78 if let SchemaType::Object(obj) = &schema.root_type {
79 if let SchemaType::Array(inner) = &obj.fields[0].field_type {
80 assert_eq!(**inner, SchemaType::String);
81 } else {
82 panic!("Expected Array type");
83 }
84 }
85 }
86
87 #[test]
88 fn test_parse_empty_array() {
89 let json = r#"{"items": []}"#;
90 let schema = parse_json_string(json, "Test", true).unwrap();
91
92 if let SchemaType::Object(obj) = &schema.root_type {
93 if let SchemaType::Array(inner) = &obj.fields[0].field_type {
94 assert_eq!(**inner, SchemaType::Any);
95 } else {
96 panic!("Expected Array type");
97 }
98 }
99 }
100
101 #[test]
102 fn test_parse_mixed_array() {
103 let json = r#"{"mixed": [1, "two", true]}"#;
104 let schema = parse_json_string(json, "Test", true).unwrap();
105
106 if let SchemaType::Object(obj) = &schema.root_type {
107 if let SchemaType::Array(inner) = &obj.fields[0].field_type {
108 assert!(matches!(**inner, SchemaType::Union(_)));
109 } else {
110 panic!("Expected Array type");
111 }
112 }
113 }
114
115 #[test]
116 fn test_nested_extraction() {
117 let json = r#"{
118 "user": {
119 "profile": {
120 "name": "John"
121 }
122 }
123 }"#;
124 let schema = parse_json_string(json, "Root", false).unwrap();
125 assert!(!schema.nested_types.is_empty());
126 }
127
128 #[test]
129 fn test_detect_uuid() {
130 let json = r#"{"id": "550e8400-e29b-41d4-a716-446655440000"}"#;
131 let schema = parse_json_string(json, "Test", true).unwrap();
132
133 if let SchemaType::Object(obj) = &schema.root_type {
134 assert_eq!(obj.fields[0].field_type, SchemaType::Uuid);
135 } else {
136 panic!("Expected Object type");
137 }
138 }
139
140 #[test]
141 fn test_detect_datetime() {
142 let json = r#"{"created": "2023-01-15T10:30:00Z"}"#;
143 let schema = parse_json_string(json, "Test", true).unwrap();
144
145 if let SchemaType::Object(obj) = &schema.root_type {
146 assert_eq!(obj.fields[0].field_type, SchemaType::DateTime);
147 } else {
148 panic!("Expected Object type");
149 }
150 }
151
152 #[test]
153 fn test_detect_date() {
154 let json = r#"{"birthday": "2023-01-15"}"#;
155 let schema = parse_json_string(json, "Test", true).unwrap();
156
157 if let SchemaType::Object(obj) = &schema.root_type {
158 assert_eq!(obj.fields[0].field_type, SchemaType::Date);
159 } else {
160 panic!("Expected Object type");
161 }
162 }
163
164 #[test]
165 fn test_detect_email() {
166 let json = r#"{"email": "user@example.com"}"#;
167 let schema = parse_json_string(json, "Test", true).unwrap();
168
169 if let SchemaType::Object(obj) = &schema.root_type {
170 assert_eq!(obj.fields[0].field_type, SchemaType::Email);
171 } else {
172 panic!("Expected Object type");
173 }
174 }
175
176 #[test]
177 fn test_detect_url() {
178 let json = r#"{"website": "https://example.com/path"}"#;
179 let schema = parse_json_string(json, "Test", true).unwrap();
180
181 if let SchemaType::Object(obj) = &schema.root_type {
182 assert_eq!(obj.fields[0].field_type, SchemaType::Url);
183 } else {
184 panic!("Expected Object type");
185 }
186 }
187
188 #[test]
189 fn test_plain_string_not_semantic() {
190 let json = r#"{"name": "John Doe"}"#;
191 let schema = parse_json_string(json, "Test", true).unwrap();
192
193 if let SchemaType::Object(obj) = &schema.root_type {
194 assert_eq!(obj.fields[0].field_type, SchemaType::String);
195 } else {
196 panic!("Expected Object type");
197 }
198 }
199}