• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

getdozer / dozer / 4829321272

pending completion
4829321272

Pull #1515

github

GitHub
Merge b6d982211 into f2ab0e6ce
Pull Request #1515: feat: Run migration only when necessary

193 of 193 new or added lines in 11 files covered. (100.0%)

35565 of 45252 relevant lines covered (78.59%)

16462.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.71
/dozer-types/src/helper.rs
1
use crate::errors::types::{DeserializationError, TypeError};
2
use crate::json_types::{serde_json_to_json_value, JsonValue};
3
use crate::types::{DozerDuration, DozerPoint, TimeUnit, DATE_FORMAT};
4
use crate::types::{Field, FieldType};
5
use chrono::{DateTime, NaiveDate};
6
use ordered_float::OrderedFloat;
7
use rust_decimal::Decimal;
8
use serde_json::Value;
9
use std::str::FromStr;
10
use std::time::Duration;
11

12
/// Used in REST APIs and query expressions for converting JSON value to `Field`
13
pub fn json_value_to_field(
85,933✔
14
    value: Value,
85,933✔
15
    typ: FieldType,
85,933✔
16
    nullable: bool,
85,933✔
17
) -> Result<Field, TypeError> {
85,933✔
18
    if nullable {
85,933✔
19
        if let Value::Null = value {
85,717✔
20
            return Ok(Field::Null);
3,084✔
21
        }
82,633✔
22
    }
216✔
23

24
    match typ {
82,849✔
25
        FieldType::UInt => serde_json::from_value(value)
157✔
26
            .map_err(DeserializationError::Json)
157✔
27
            .map(Field::UInt),
157✔
28
        FieldType::U128 => match value {
×
29
            Value::String(str) => return Field::from_str(str.as_str(), typ, nullable),
×
30
            _ => Err(DeserializationError::Custom(
×
31
                "Json value type does not match field type"
×
32
                    .to_string()
×
33
                    .into(),
×
34
            )),
×
35
        },
36
        FieldType::Int => serde_json::from_value(value)
33,121✔
37
            .map_err(DeserializationError::Json)
33,121✔
38
            .map(Field::Int),
33,121✔
39
        FieldType::I128 => match value {
×
40
            Value::String(str) => return Field::from_str(str.as_str(), typ, nullable),
×
41
            _ => Err(DeserializationError::Custom(
×
42
                "Json value type does not match field type"
×
43
                    .to_string()
×
44
                    .into(),
×
45
            )),
×
46
        },
47
        FieldType::Float => serde_json::from_value(value)
1✔
48
            .map_err(DeserializationError::Json)
1✔
49
            .map(Field::Float),
1✔
50
        FieldType::Boolean => serde_json::from_value(value)
1✔
51
            .map_err(DeserializationError::Json)
1✔
52
            .map(Field::Boolean),
1✔
53
        FieldType::String => serde_json::from_value(value)
49,537✔
54
            .map_err(DeserializationError::Json)
49,537✔
55
            .map(Field::String),
49,537✔
56
        FieldType::Text => serde_json::from_value(value)
25✔
57
            .map_err(DeserializationError::Json)
25✔
58
            .map(Field::Text),
25✔
59
        FieldType::Binary => serde_json::from_value(value)
1✔
60
            .map_err(DeserializationError::Json)
1✔
61
            .map(Field::Binary),
1✔
62
        FieldType::Decimal => match value {
1✔
63
            Value::String(str) => return Field::from_str(str.as_str(), typ, nullable),
1✔
64
            Value::Number(number) => return Field::from_str(&number.to_string(), typ, nullable),
×
65
            _ => Err(DeserializationError::Custom(
×
66
                "Json value type does not match field type"
×
67
                    .to_string()
×
68
                    .into(),
×
69
            )),
×
70
        },
71
        FieldType::Timestamp => match value {
1✔
72
            Value::String(str) => return Field::from_str(str.as_str(), typ, nullable),
1✔
73
            _ => Err(DeserializationError::Custom(
×
74
                "Json value type does not match field type"
×
75
                    .to_string()
×
76
                    .into(),
×
77
            )),
×
78
        },
79
        FieldType::Date => match value {
1✔
80
            Value::String(str) => return Field::from_str(str.as_str(), typ, nullable),
1✔
81
            _ => Err(DeserializationError::Custom(
×
82
                "Json value type does not match field type"
×
83
                    .to_string()
×
84
                    .into(),
×
85
            )),
×
86
        },
87
        FieldType::Json => Ok(Field::Json(
88
            serde_json_to_json_value(value).map_err(TypeError::DeserializationError)?,
1✔
89
        )),
90
        FieldType::Point => serde_json::from_value(value)
1✔
91
            .map_err(DeserializationError::Json)
1✔
92
            .map(Field::Point),
1✔
93
        FieldType::Duration => match value.get("value") {
1✔
94
            Some(Value::String(v_val)) => match value.get("time_unit") {
1✔
95
                Some(Value::String(tu_val)) => {
1✔
96
                    let time_unit = TimeUnit::from_str(tu_val)?;
1✔
97
                    return match time_unit {
1✔
98
                        TimeUnit::Seconds => {
99
                            let dur = u64::from_str(v_val).unwrap();
×
100
                            Ok(Field::Duration(DozerDuration(
×
101
                                Duration::from_secs(dur),
×
102
                                time_unit,
×
103
                            )))
×
104
                        }
105
                        TimeUnit::Milliseconds => {
106
                            let dur = u64::from_str(v_val).unwrap();
×
107
                            Ok(Field::Duration(DozerDuration(
×
108
                                Duration::from_millis(dur),
×
109
                                time_unit,
×
110
                            )))
×
111
                        }
112
                        TimeUnit::Microseconds => {
113
                            let dur = u64::from_str(v_val).unwrap();
×
114
                            Ok(Field::Duration(DozerDuration(
×
115
                                Duration::from_micros(dur),
×
116
                                time_unit,
×
117
                            )))
×
118
                        }
119
                        TimeUnit::Nanoseconds => {
120
                            Ok(Field::Duration(DozerDuration::from_str(v_val).unwrap()))
1✔
121
                        }
122
                    };
123
                }
124
                _ => Err(DeserializationError::Custom(
×
125
                    "Json value type does not match field type"
×
126
                        .to_string()
×
127
                        .into(),
×
128
                )),
×
129
            },
130
            _ => Err(DeserializationError::Custom(
×
131
                "Json value type does not match field type"
×
132
                    .to_string()
×
133
                    .into(),
×
134
            )),
×
135
        },
136
    }
137
    .map_err(TypeError::DeserializationError)
82,845✔
138
}
85,933✔
139

140
impl Field {
141
    pub fn from_str(value: &str, typ: FieldType, nullable: bool) -> Result<Field, TypeError> {
84✔
142
        match typ {
84✔
143
            FieldType::UInt => {
144
                if nullable && (value.is_empty() || value == "null") {
17✔
145
                    Ok(Field::Null)
2✔
146
                } else {
147
                    value.parse::<u64>().map(Field::UInt).map_err(|_| {
15✔
148
                        TypeError::InvalidFieldValue {
2✔
149
                            field_type: typ,
2✔
150
                            nullable,
2✔
151
                            value: value.to_string(),
2✔
152
                        }
2✔
153
                    })
15✔
154
                }
155
            }
156
            FieldType::U128 => {
157
                if nullable && (value.is_empty() || value == "null") {
5✔
158
                    Ok(Field::Null)
2✔
159
                } else {
160
                    value.parse::<u128>().map(Field::U128).map_err(|_| {
3✔
161
                        TypeError::InvalidFieldValue {
2✔
162
                            field_type: typ,
2✔
163
                            nullable,
2✔
164
                            value: value.to_string(),
2✔
165
                        }
2✔
166
                    })
3✔
167
                }
168
            }
169
            FieldType::Int => {
170
                if nullable && (value.is_empty() || value == "null") {
5✔
171
                    Ok(Field::Null)
2✔
172
                } else {
173
                    value
3✔
174
                        .parse::<i64>()
3✔
175
                        .map(Field::Int)
3✔
176
                        .map_err(|_| TypeError::InvalidFieldValue {
3✔
177
                            field_type: typ,
2✔
178
                            nullable,
2✔
179
                            value: value.to_string(),
2✔
180
                        })
3✔
181
                }
182
            }
183
            FieldType::I128 => {
184
                if nullable && (value.is_empty() || value == "null") {
5✔
185
                    Ok(Field::Null)
2✔
186
                } else {
187
                    value.parse::<i128>().map(Field::I128).map_err(|_| {
3✔
188
                        TypeError::InvalidFieldValue {
2✔
189
                            field_type: typ,
2✔
190
                            nullable,
2✔
191
                            value: value.to_string(),
2✔
192
                        }
2✔
193
                    })
3✔
194
                }
195
            }
196
            FieldType::Float => {
197
                if nullable && (value.is_empty() || value == "null") {
5✔
198
                    Ok(Field::Null)
2✔
199
                } else {
200
                    value
3✔
201
                        .parse::<OrderedFloat<f64>>()
3✔
202
                        .map(Field::Float)
3✔
203
                        .map_err(|_| TypeError::InvalidFieldValue {
3✔
204
                            field_type: typ,
2✔
205
                            nullable,
2✔
206
                            value: value.to_string(),
2✔
207
                        })
3✔
208
                }
209
            }
210
            FieldType::Boolean => {
211
                if nullable && (value.is_empty() || value == "null") {
6✔
212
                    Ok(Field::Null)
2✔
213
                } else {
214
                    value.parse::<bool>().map(Field::Boolean).map_err(|_| {
4✔
215
                        TypeError::InvalidFieldValue {
2✔
216
                            field_type: typ,
2✔
217
                            nullable,
2✔
218
                            value: value.to_string(),
2✔
219
                        }
2✔
220
                    })
4✔
221
                }
222
            }
223
            FieldType::String => Ok(Field::String(value.to_string())),
3✔
224
            FieldType::Text => Ok(Field::Text(value.to_string())),
3✔
225
            FieldType::Binary => {
226
                if nullable && (value.is_empty() || value == "null") {
4✔
227
                    Ok(Field::Null)
2✔
228
                } else {
229
                    Err(TypeError::InvalidFieldValue {
2✔
230
                        field_type: typ,
2✔
231
                        nullable,
2✔
232
                        value: value.to_string(),
2✔
233
                    })
2✔
234
                }
235
            }
236
            FieldType::Decimal => {
237
                if nullable && (value.is_empty() || value == "null") {
6✔
238
                    Ok(Field::Null)
2✔
239
                } else {
240
                    Decimal::from_str(value).map(Field::Decimal).map_err(|_| {
4✔
241
                        TypeError::InvalidFieldValue {
2✔
242
                            field_type: typ,
2✔
243
                            nullable,
2✔
244
                            value: value.to_string(),
2✔
245
                        }
2✔
246
                    })
4✔
247
                }
248
            }
249
            FieldType::Timestamp => {
250
                if nullable && (value.is_empty() || value == "null") {
6✔
251
                    Ok(Field::Null)
2✔
252
                } else {
253
                    DateTime::parse_from_rfc3339(value)
4✔
254
                        .map(Field::Timestamp)
4✔
255
                        .map_err(|_| TypeError::InvalidFieldValue {
4✔
256
                            field_type: typ,
2✔
257
                            nullable,
2✔
258
                            value: value.to_string(),
2✔
259
                        })
4✔
260
                }
261
            }
262
            FieldType::Date => {
263
                if nullable && (value.is_empty() || value == "null") {
6✔
264
                    Ok(Field::Null)
2✔
265
                } else {
266
                    NaiveDate::parse_from_str(value, DATE_FORMAT)
4✔
267
                        .map(Field::Date)
4✔
268
                        .map_err(|_| TypeError::InvalidFieldValue {
4✔
269
                            field_type: typ,
2✔
270
                            nullable,
2✔
271
                            value: value.to_string(),
2✔
272
                        })
4✔
273
                }
274
            }
275
            FieldType::Json => {
276
                if nullable && (value.is_empty() || value == "null") {
4✔
277
                    Ok(Field::Null)
3✔
278
                } else {
279
                    JsonValue::from_str(value).map(Field::Json).map_err(|_| {
1✔
280
                        TypeError::InvalidFieldValue {
×
281
                            field_type: typ,
×
282
                            nullable,
×
283
                            value: value.to_string(),
×
284
                        }
×
285
                    })
1✔
286
                }
287
            }
288
            FieldType::Point => {
289
                if nullable && (value.is_empty() || value == "null") {
5✔
290
                    Ok(Field::Null)
2✔
291
                } else {
292
                    value.parse::<DozerPoint>().map(Field::Point)
3✔
293
                }
294
            }
295
            FieldType::Duration => {
296
                if nullable && (value.is_empty() || value == "null") {
4✔
297
                    Ok(Field::Null)
2✔
298
                } else {
299
                    value.parse::<DozerDuration>().map(Field::Duration)
2✔
300
                }
301
            }
302
        }
303
    }
84✔
304
}
305

306
#[cfg(test)]
307
mod tests {
308
    use chrono::FixedOffset;
309
    use geo::Point;
310
    use rust_decimal::prelude::FromPrimitive;
311
    use std::collections::BTreeMap;
312

313
    use super::*;
314

315
    #[test]
1✔
316
    fn test_field_from_str() {
1✔
317
        let ok_cases = [
1✔
318
            ("1", FieldType::UInt, false, Field::UInt(1)),
1✔
319
            ("1", FieldType::U128, false, Field::U128(1)),
1✔
320
            ("1", FieldType::Int, false, Field::Int(1)),
1✔
321
            ("1", FieldType::I128, false, Field::I128(1)),
1✔
322
            ("1.1", FieldType::Float, false, Field::Float(1.1.into())),
1✔
323
            ("true", FieldType::Boolean, false, Field::Boolean(true)),
1✔
324
            ("false", FieldType::Boolean, false, Field::Boolean(false)),
1✔
325
            (
1✔
326
                "hello",
1✔
327
                FieldType::String,
1✔
328
                false,
1✔
329
                Field::String("hello".to_string()),
1✔
330
            ),
1✔
331
            (
1✔
332
                "hello",
1✔
333
                FieldType::Text,
1✔
334
                false,
1✔
335
                Field::Text("hello".to_string()),
1✔
336
            ),
1✔
337
            (
1✔
338
                "1.1",
1✔
339
                FieldType::Decimal,
1✔
340
                false,
1✔
341
                Field::Decimal(Decimal::from_f64(1.1).unwrap()),
1✔
342
            ),
1✔
343
            (
1✔
344
                "2020-01-01T00:00:00Z",
1✔
345
                FieldType::Timestamp,
1✔
346
                false,
1✔
347
                Field::Timestamp(
1✔
348
                    "2020-01-01T00:00:00Z"
1✔
349
                        .parse::<DateTime<FixedOffset>>()
1✔
350
                        .unwrap(),
1✔
351
                ),
1✔
352
            ),
1✔
353
            (
1✔
354
                "2020-01-01",
1✔
355
                FieldType::Date,
1✔
356
                false,
1✔
357
                Field::Date("2020-01-01".parse::<NaiveDate>().unwrap()),
1✔
358
            ),
1✔
359
            (
1✔
360
                "(1,1)",
1✔
361
                FieldType::Point,
1✔
362
                false,
1✔
363
                Field::Point(DozerPoint(Point::new(OrderedFloat(1.0), OrderedFloat(1.0)))),
1✔
364
            ),
1✔
365
            (
1✔
366
                "{\"abc\":\"foo\"}",
1✔
367
                FieldType::Json,
1✔
368
                false,
1✔
369
                Field::Json(JsonValue::Object(BTreeMap::from([(
1✔
370
                    String::from("abc"),
1✔
371
                    JsonValue::String(String::from("foo")),
1✔
372
                )]))),
1✔
373
            ),
1✔
374
            ("null", FieldType::UInt, true, Field::Null),
1✔
375
            ("null", FieldType::U128, true, Field::Null),
1✔
376
            ("null", FieldType::Int, true, Field::Null),
1✔
377
            ("null", FieldType::I128, true, Field::Null),
1✔
378
            ("null", FieldType::Float, true, Field::Null),
1✔
379
            ("null", FieldType::Boolean, true, Field::Null),
1✔
380
            (
1✔
381
                "null",
1✔
382
                FieldType::String,
1✔
383
                true,
1✔
384
                Field::String("null".to_string()),
1✔
385
            ),
1✔
386
            (
1✔
387
                "null",
1✔
388
                FieldType::Text,
1✔
389
                true,
1✔
390
                Field::Text("null".to_string()),
1✔
391
            ),
1✔
392
            ("null", FieldType::Binary, true, Field::Null),
1✔
393
            ("null", FieldType::Decimal, true, Field::Null),
1✔
394
            ("null", FieldType::Timestamp, true, Field::Null),
1✔
395
            ("null", FieldType::Date, true, Field::Null),
1✔
396
            ("null", FieldType::Json, true, Field::Null),
1✔
397
            ("null", FieldType::Point, true, Field::Null),
1✔
398
            ("null", FieldType::Json, true, Field::Null),
1✔
399
            ("null", FieldType::Duration, true, Field::Null),
1✔
400
            ("", FieldType::UInt, true, Field::Null),
1✔
401
            ("", FieldType::U128, true, Field::Null),
1✔
402
            ("", FieldType::Int, true, Field::Null),
1✔
403
            ("", FieldType::I128, true, Field::Null),
1✔
404
            ("", FieldType::Float, true, Field::Null),
1✔
405
            ("", FieldType::Boolean, true, Field::Null),
1✔
406
            ("", FieldType::String, true, Field::String(String::new())),
1✔
407
            ("", FieldType::Text, true, Field::Text(String::new())),
1✔
408
            ("", FieldType::Binary, true, Field::Null),
1✔
409
            ("", FieldType::Decimal, true, Field::Null),
1✔
410
            ("", FieldType::Timestamp, true, Field::Null),
1✔
411
            ("", FieldType::Date, true, Field::Null),
1✔
412
            ("", FieldType::Json, true, Field::Null),
1✔
413
            ("", FieldType::Point, true, Field::Null),
1✔
414
            ("", FieldType::Duration, true, Field::Null),
1✔
415
        ];
1✔
416

417
        for case in ok_cases {
46✔
418
            assert_eq!(Field::from_str(case.0, case.1, case.2).unwrap(), case.3);
45✔
419
        }
420

421
        let err_cases = [
1✔
422
            ("null", FieldType::UInt, false),
1✔
423
            ("null", FieldType::U128, false),
1✔
424
            ("null", FieldType::Int, false),
1✔
425
            ("null", FieldType::I128, false),
1✔
426
            ("null", FieldType::Float, false),
1✔
427
            ("null", FieldType::Boolean, false),
1✔
428
            ("null", FieldType::Binary, false),
1✔
429
            ("null", FieldType::Decimal, false),
1✔
430
            ("null", FieldType::Timestamp, false),
1✔
431
            ("null", FieldType::Date, false),
1✔
432
            ("null", FieldType::Point, false),
1✔
433
            ("null", FieldType::Duration, false),
1✔
434
            ("", FieldType::UInt, false),
1✔
435
            ("", FieldType::U128, false),
1✔
436
            ("", FieldType::Int, false),
1✔
437
            ("", FieldType::I128, false),
1✔
438
            ("", FieldType::Float, false),
1✔
439
            ("", FieldType::Boolean, false),
1✔
440
            ("", FieldType::Binary, false),
1✔
441
            ("", FieldType::Decimal, false),
1✔
442
            ("", FieldType::Timestamp, false),
1✔
443
            ("", FieldType::Date, false),
1✔
444
            ("", FieldType::Point, false),
1✔
445
            ("", FieldType::Duration, false),
1✔
446
        ];
1✔
447
        for err_case in err_cases {
25✔
448
            assert!(Field::from_str(err_case.0, err_case.1, err_case.2).is_err());
24✔
449
        }
450
    }
1✔
451
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc