1use std::str::FromStr;
21use std::sync::Arc;
22
23use serde::{Deserialize, Serialize};
24use serde_with::{DefaultOnNull, serde_as};
25
26use crate::error::invalid_data;
27use crate::{Error, Result};
28
29#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
31#[serde(transparent)]
32pub struct NameMapping {
33 root: Vec<MappedField>,
34}
35
36impl NameMapping {
37 pub fn new(fields: Vec<MappedField>) -> Self {
39 Self { root: fields }
40 }
41
42 pub fn fields(&self) -> &[MappedField] {
44 &self.root
45 }
46}
47
48impl FromStr for NameMapping {
49 type Err = Error;
50
51 fn from_str(value: &str) -> Result<Self> {
53 serde_json::from_str(value).map_err(|error| {
54 invalid_data!("Failed to parse value as a NameMapping").with_source(error)
55 })
56 }
57}
58
59#[serde_as]
61#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
62#[serde(rename_all = "kebab-case")]
63pub struct MappedField {
64 #[serde(skip_serializing_if = "Option::is_none")]
65 field_id: Option<i32>,
66 names: Vec<String>,
67 #[serde(default)]
68 #[serde(skip_serializing_if = "Vec::is_empty")]
69 #[serde_as(deserialize_as = "DefaultOnNull")]
70 fields: Vec<Arc<MappedField>>,
71}
72
73impl MappedField {
74 pub fn new(field_id: Option<i32>, names: Vec<String>, fields: Vec<MappedField>) -> Self {
76 Self {
77 field_id,
78 names,
79 fields: fields.into_iter().map(Arc::new).collect(),
80 }
81 }
82
83 pub fn field_id(&self) -> Option<i32> {
85 self.field_id
86 }
87
88 pub fn names(&self) -> &[String] {
90 &self.names
91 }
92
93 pub fn fields(&self) -> &[Arc<MappedField>] {
95 &self.fields
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn test_json_mapped_field_deserialization() {
105 let expected = MappedField {
106 field_id: Some(1),
107 names: vec!["id".to_string(), "record_id".to_string()],
108 fields: vec![],
109 };
110 let mapped_field = r#"
111 {
112 "field-id": 1,
113 "names": ["id", "record_id"]
114 }
115 "#;
116
117 let mapped_field: MappedField = serde_json::from_str(mapped_field).unwrap();
118 assert_eq!(mapped_field, expected);
119
120 let mapped_field_with_null_fields = r#"
121 {
122 "field-id": 1,
123 "names": ["id", "record_id"],
124 "fields": null
125 }
126 "#;
127
128 let mapped_field_with_null_fields: MappedField =
129 serde_json::from_str(mapped_field_with_null_fields).unwrap();
130 assert_eq!(mapped_field_with_null_fields, expected);
131 }
132
133 #[test]
134 fn test_json_mapped_field_no_names_deserialization() {
135 let expected = MappedField {
136 field_id: Some(1),
137 names: vec![],
138 fields: vec![],
139 };
140 let mapped_field = r#"
141 {
142 "field-id": 1,
143 "names": []
144 }
145 "#;
146
147 let mapped_field: MappedField = serde_json::from_str(mapped_field).unwrap();
148 assert_eq!(mapped_field, expected);
149
150 let mapped_field_with_null_fields = r#"
151 {
152 "field-id": 1,
153 "names": [],
154 "fields": null
155 }
156 "#;
157
158 let mapped_field_with_null_fields: MappedField =
159 serde_json::from_str(mapped_field_with_null_fields).unwrap();
160 assert_eq!(mapped_field_with_null_fields, expected);
161 }
162
163 #[test]
164 fn test_json_mapped_field_no_field_id_deserialization() {
165 let expected = MappedField {
166 field_id: None,
167 names: vec!["id".to_string(), "record_id".to_string()],
168 fields: vec![],
169 };
170 let mapped_field = r#"
171 {
172 "names": ["id", "record_id"]
173 }
174 "#;
175
176 let mapped_field: MappedField = serde_json::from_str(mapped_field).unwrap();
177 assert_eq!(mapped_field, expected);
178
179 let mapped_field_with_null_fields = r#"
180 {
181 "names": ["id", "record_id"],
182 "fields": null
183 }
184 "#;
185
186 let mapped_field_with_null_fields: MappedField =
187 serde_json::from_str(mapped_field_with_null_fields).unwrap();
188 assert_eq!(mapped_field_with_null_fields, expected);
189 }
190
191 #[test]
192 fn test_json_name_mapping_deserialization() {
193 let name_mapping = r#"
194 [
195 {
196 "field-id": 1,
197 "names": [
198 "id",
199 "record_id"
200 ]
201 },
202 {
203 "field-id": 2,
204 "names": [
205 "data"
206 ]
207 },
208 {
209 "field-id": 3,
210 "names": [
211 "location"
212 ],
213 "fields": [
214 {
215 "field-id": 4,
216 "names": [
217 "latitude",
218 "lat"
219 ]
220 },
221 {
222 "field-id": 5,
223 "names": [
224 "longitude",
225 "long"
226 ]
227 }
228 ]
229 }
230 ]
231 "#;
232
233 let name_mapping: NameMapping = serde_json::from_str(name_mapping).unwrap();
234 assert_eq!(name_mapping, NameMapping {
235 root: vec![
236 MappedField {
237 field_id: Some(1),
238 names: vec!["id".to_string(), "record_id".to_string()],
239 fields: vec![]
240 },
241 MappedField {
242 field_id: Some(2),
243 names: vec!["data".to_string()],
244 fields: vec![]
245 },
246 MappedField {
247 field_id: Some(3),
248 names: vec!["location".to_string()],
249 fields: vec![
250 MappedField {
251 field_id: Some(4),
252 names: vec!["latitude".to_string(), "lat".to_string()],
253 fields: vec![]
254 }
255 .into(),
256 MappedField {
257 field_id: Some(5),
258 names: vec!["longitude".to_string(), "long".to_string()],
259 fields: vec![]
260 }
261 .into(),
262 ]
263 }
264 ],
265 });
266 }
267
268 #[test]
269 fn test_json_name_mapping_serialization() {
270 let name_mapping = NameMapping {
271 root: vec![
272 MappedField {
273 field_id: None,
274 names: vec!["foo".to_string()],
275 fields: vec![],
276 },
277 MappedField {
278 field_id: Some(2),
279 names: vec!["bar".to_string()],
280 fields: vec![],
281 },
282 MappedField {
283 field_id: Some(3),
284 names: vec!["baz".to_string()],
285 fields: vec![],
286 },
287 MappedField {
288 field_id: Some(4),
289 names: vec!["qux".to_string()],
290 fields: vec![
291 MappedField {
292 field_id: Some(5),
293 names: vec!["element".to_string()],
294 fields: vec![],
295 }
296 .into(),
297 ],
298 },
299 MappedField {
300 field_id: Some(6),
301 names: vec!["quux".to_string()],
302 fields: vec![
303 MappedField {
304 field_id: Some(7),
305 names: vec!["key".to_string()],
306 fields: vec![],
307 }
308 .into(),
309 MappedField {
310 field_id: Some(8),
311 names: vec!["value".to_string()],
312 fields: vec![
313 MappedField {
314 field_id: Some(9),
315 names: vec!["key".to_string()],
316 fields: vec![],
317 }
318 .into(),
319 MappedField {
320 field_id: Some(10),
321 names: vec!["value".to_string()],
322 fields: vec![],
323 }
324 .into(),
325 ],
326 }
327 .into(),
328 ],
329 },
330 MappedField {
331 field_id: Some(11),
332 names: vec!["location".to_string()],
333 fields: vec![
334 MappedField {
335 field_id: Some(12),
336 names: vec!["element".to_string()],
337 fields: vec![
338 MappedField {
339 field_id: Some(13),
340 names: vec!["latitude".to_string()],
341 fields: vec![],
342 }
343 .into(),
344 MappedField {
345 field_id: Some(14),
346 names: vec!["longitude".to_string()],
347 fields: vec![],
348 }
349 .into(),
350 ],
351 }
352 .into(),
353 ],
354 },
355 MappedField {
356 field_id: Some(15),
357 names: vec!["person".to_string()],
358 fields: vec![
359 MappedField {
360 field_id: Some(16),
361 names: vec!["name".to_string()],
362 fields: vec![],
363 }
364 .into(),
365 MappedField {
366 field_id: Some(17),
367 names: vec!["age".to_string()],
368 fields: vec![],
369 }
370 .into(),
371 ],
372 },
373 ],
374 };
375 let expected = r#"[{"names":["foo"]},{"field-id":2,"names":["bar"]},{"field-id":3,"names":["baz"]},{"field-id":4,"names":["qux"],"fields":[{"field-id":5,"names":["element"]}]},{"field-id":6,"names":["quux"],"fields":[{"field-id":7,"names":["key"]},{"field-id":8,"names":["value"],"fields":[{"field-id":9,"names":["key"]},{"field-id":10,"names":["value"]}]}]},{"field-id":11,"names":["location"],"fields":[{"field-id":12,"names":["element"],"fields":[{"field-id":13,"names":["latitude"]},{"field-id":14,"names":["longitude"]}]}]},{"field-id":15,"names":["person"],"fields":[{"field-id":16,"names":["name"]},{"field-id":17,"names":["age"]}]}]"#;
376 assert_eq!(serde_json::to_string(&name_mapping).unwrap(), expected);
377 }
378}