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