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

geo-engine / geoengine / 5869096654

15 Aug 2023 02:59PM UTC coverage: 89.79% (+0.3%) from 89.481%
5869096654

push

github

web-flow
Merge pull request #851 from geo-engine/pg-dataset-metadata-mapping

Pg dataset metadata mapping

1982 of 1982 new or added lines in 9 files covered. (100.0%)

106148 of 118218 relevant lines covered (89.79%)

61246.75 hits per line

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

96.45
/services/src/api/model/db_types.rs
1
use super::{
2
    datatypes::{
3
        BoundingBox2D, Breakpoint, ClassificationMeasurement, Colorizer, ContinuousMeasurement,
4
        Coordinate2D, DateTimeParseFormat, DefaultColors, FeatureDataType, LinearGradient,
5
        LogarithmicGradient, Measurement, MultiLineString, MultiPoint, MultiPolygon, NoGeometry,
6
        OverUnderColors, Palette, RgbaColor, SpatialReferenceOption, TimeInstance, TimeInterval,
7
        TimeStep, VectorDataType,
8
    },
9
    operators::{
10
        CsvHeader, FileNotFoundHandling, FormatSpecifics, GdalConfigOption,
11
        GdalDatasetGeoTransform, GdalDatasetParameters, GdalMetaDataList, GdalMetaDataRegular,
12
        GdalMetaDataStatic, GdalMetadataMapping, GdalMetadataNetCdfCf, GdalSourceTimePlaceholder,
13
        MockDatasetDataSourceLoadingInfo, MockMetaData, OgrMetaData, OgrSourceColumnSpec,
14
        OgrSourceDataset, OgrSourceDatasetTimeType, OgrSourceDurationSpec, OgrSourceErrorSpec,
15
        OgrSourceTimeFormat, PlotResultDescriptor, RasterResultDescriptor, TypedGeometry,
16
        TypedResultDescriptor, UnixTimeStampType, VectorColumnInfo, VectorResultDescriptor,
17
    },
18
};
19
use crate::{
20
    datasets::storage::MetaDataDefinition,
21
    error::Error,
22
    projects::{
23
        ColorParam, DerivedColor, DerivedNumber, LineSymbology, NumberParam, PointSymbology,
24
        PolygonSymbology, RasterSymbology, Symbology,
25
    },
26
};
27
use fallible_iterator::FallibleIterator;
28
use geoengine_datatypes::primitives::CacheTtlSeconds;
29
use ordered_float::NotNan;
30
use postgres_types::{FromSql, ToSql};
31
use std::{collections::HashMap, marker::PhantomData};
32

33
#[derive(Debug, ToSql, FromSql)]
9,928✔
34
#[postgres(name = "DefaultColors")]
35
pub struct DefaultColorsDbType {
36
    pub default_color: Option<RgbaColor>,
37
    pub over_color: Option<RgbaColor>,
38
    pub under_color: Option<RgbaColor>,
39
}
40

41
impl From<&DefaultColors> for DefaultColorsDbType {
42
    fn from(value: &DefaultColors) -> Self {
15✔
43
        match value {
15✔
44
            DefaultColors::DefaultColor { default_color } => Self {
1✔
45
                default_color: Some(*default_color),
1✔
46
                over_color: None,
1✔
47
                under_color: None,
1✔
48
            },
1✔
49
            DefaultColors::OverUnder(over_under) => Self {
14✔
50
                default_color: None,
14✔
51
                over_color: Some(over_under.over_color),
14✔
52
                under_color: Some(over_under.under_color),
14✔
53
            },
14✔
54
        }
55
    }
15✔
56
}
57

58
impl TryFrom<DefaultColorsDbType> for DefaultColors {
59
    type Error = Error;
60

61
    fn try_from(value: DefaultColorsDbType) -> Result<Self, Self::Error> {
62
        match (value.default_color, value.over_color, value.under_color) {
13✔
63
            (Some(default_color), None, None) => Ok(Self::DefaultColor { default_color }),
1✔
64
            (None, Some(over_color), Some(under_color)) => Ok(Self::OverUnder(OverUnderColors {
12✔
65
                over_color,
12✔
66
                under_color,
12✔
67
            })),
12✔
68
            _ => Err(Error::UnexpectedInvalidDbTypeConversion),
×
69
        }
70
    }
13✔
71
}
72

73
#[derive(Debug, ToSql, FromSql)]
14,868✔
74
#[postgres(name = "Colorizer")]
75
pub struct ColorizerDbType {
76
    r#type: ColorizerTypeDbType,
77
    breakpoints: Option<Vec<Breakpoint>>,
78
    no_data_color: Option<RgbaColor>,
79
    color_fields: Option<DefaultColorsDbType>,
80
    default_color: Option<RgbaColor>,
81
}
82

83
#[derive(Debug, PartialEq, ToSql, FromSql)]
12,410✔
84
// TODO: use #[postgres(rename_all = "camelCase")]
85
#[postgres(name = "ColorizerType")]
86
pub enum ColorizerTypeDbType {
87
    LinearGradient,
88
    LogarithmicGradient,
89
    Palette,
90
    Rgba,
91
}
92

93
impl From<&Colorizer> for ColorizerDbType {
94
    fn from(value: &Colorizer) -> Self {
25✔
95
        match value {
25✔
96
            Colorizer::LinearGradient(gradient) => ColorizerDbType {
12✔
97
                r#type: ColorizerTypeDbType::LinearGradient,
12✔
98
                breakpoints: Some(gradient.breakpoints.clone()),
12✔
99
                no_data_color: Some(gradient.no_data_color),
12✔
100
                color_fields: Some((&gradient.color_fields).into()),
12✔
101
                default_color: None,
12✔
102
            },
12✔
103
            Colorizer::LogarithmicGradient(gradient) => ColorizerDbType {
1✔
104
                r#type: ColorizerTypeDbType::LogarithmicGradient,
1✔
105
                breakpoints: Some(gradient.breakpoints.clone()),
1✔
106
                no_data_color: Some(gradient.no_data_color),
1✔
107
                color_fields: Some((&gradient.color_fields).into()),
1✔
108
                default_color: None,
1✔
109
            },
1✔
110
            Colorizer::Palette {
111
                colors,
3✔
112
                no_data_color,
3✔
113
                default_color,
3✔
114
            } => ColorizerDbType {
3✔
115
                r#type: ColorizerTypeDbType::Palette,
3✔
116
                breakpoints: Some(
3✔
117
                    colors
3✔
118
                        .0
3✔
119
                        .iter()
3✔
120
                        .map(|(k, v)| Breakpoint {
276✔
121
                            value: (*k).into(),
276✔
122
                            color: *v,
276✔
123
                        })
276✔
124
                        .collect(),
3✔
125
                ),
3✔
126
                no_data_color: Some(*no_data_color),
3✔
127
                color_fields: None,
3✔
128
                default_color: Some(*default_color),
3✔
129
            },
3✔
130
            Colorizer::Rgba => ColorizerDbType {
9✔
131
                r#type: ColorizerTypeDbType::Rgba,
9✔
132
                breakpoints: None,
9✔
133
                no_data_color: None,
9✔
134
                color_fields: None,
9✔
135
                default_color: None,
9✔
136
            },
9✔
137
        }
138
    }
25✔
139
}
140

141
impl TryFrom<ColorizerDbType> for Colorizer {
142
    type Error = Error;
143

144
    fn try_from(value: ColorizerDbType) -> Result<Self, Self::Error> {
145
        match value {
10✔
146
            ColorizerDbType {
147
                r#type: ColorizerTypeDbType::LinearGradient,
148
                breakpoints: Some(breakpoints),
10✔
149
                no_data_color: Some(no_data_color),
10✔
150
                color_fields: Some(color_fields),
10✔
151
                default_color: None,
10✔
152
            } => Ok(Self::LinearGradient(LinearGradient {
10✔
153
                breakpoints,
10✔
154
                no_data_color,
10✔
155
                color_fields: color_fields.try_into()?,
10✔
156
            })),
157
            ColorizerDbType {
158
                r#type: ColorizerTypeDbType::LogarithmicGradient,
159
                breakpoints: Some(breakpoints),
1✔
160
                no_data_color: Some(no_data_color),
1✔
161
                color_fields: Some(color_fields),
1✔
162
                default_color: None,
1✔
163
            } => Ok(Self::LogarithmicGradient(LogarithmicGradient {
1✔
164
                breakpoints,
1✔
165
                no_data_color,
1✔
166
                color_fields: color_fields.try_into()?,
1✔
167
            })),
168
            ColorizerDbType {
169
                r#type: ColorizerTypeDbType::Palette,
170
                breakpoints: Some(breakpoints),
1✔
171
                no_data_color: Some(no_data_color),
1✔
172
                color_fields: None,
1✔
173
                default_color: Some(default_color),
1✔
174
            } => Ok(Self::Palette {
1✔
175
                colors: Palette(
1✔
176
                    breakpoints
1✔
177
                        .into_iter()
1✔
178
                        .map(|b| (NotNan::<f64>::from(b.value), b.color))
3✔
179
                        .collect(),
1✔
180
                ),
1✔
181
                no_data_color,
1✔
182
                default_color,
1✔
183
            }),
1✔
184
            ColorizerDbType {
185
                r#type: ColorizerTypeDbType::Rgba,
186
                breakpoints: None,
187
                no_data_color: None,
188
                color_fields: None,
189
                default_color: None,
190
            } => Ok(Self::Rgba),
13✔
191
            _ => Err(Error::UnexpectedInvalidDbTypeConversion),
×
192
        }
193
    }
25✔
194
}
195

196
#[derive(Debug, ToSql, FromSql)]
9,060✔
197
#[postgres(name = "ColorParam")]
198
pub struct ColorParamDbType {
199
    color: Option<RgbaColor>,
200
    attribute: Option<String>,
201
    colorizer: Option<Colorizer>,
202
}
203

204
impl From<&ColorParam> for ColorParamDbType {
205
    fn from(value: &ColorParam) -> Self {
36✔
206
        match value {
36✔
207
            ColorParam::Static { color } => Self {
35✔
208
                color: Some((*color).into()),
35✔
209
                attribute: None,
35✔
210
                colorizer: None,
35✔
211
            },
35✔
212
            ColorParam::Derived(DerivedColor {
213
                attribute,
1✔
214
                colorizer,
1✔
215
            }) => Self {
1✔
216
                color: None,
1✔
217
                attribute: Some(attribute.clone()),
1✔
218
                colorizer: Some(colorizer.clone()),
1✔
219
            },
1✔
220
        }
221
    }
36✔
222
}
223

224
impl TryFrom<ColorParamDbType> for ColorParam {
225
    type Error = Error;
226

227
    fn try_from(value: ColorParamDbType) -> Result<Self, Self::Error> {
228
        match value {
1✔
229
            ColorParamDbType {
230
                color: Some(color),
29✔
231
                attribute: None,
29✔
232
                colorizer: None,
29✔
233
            } => Ok(Self::Static {
29✔
234
                color: color.into(),
29✔
235
            }),
29✔
236
            ColorParamDbType {
237
                color: None,
238
                attribute: Some(attribute),
1✔
239
                colorizer: Some(colorizer),
1✔
240
            } => Ok(Self::Derived(DerivedColor {
1✔
241
                attribute,
1✔
242
                colorizer,
1✔
243
            })),
1✔
244
            _ => Err(Error::UnexpectedInvalidDbTypeConversion),
×
245
        }
246
    }
30✔
247
}
248

249
#[derive(Debug, ToSql, FromSql)]
7,215✔
250
#[postgres(name = "NumberParam")]
251
pub struct NumberParamDbType {
252
    value: Option<i64>,
253
    attribute: Option<String>,
254
    factor: Option<f64>,
255
    default_value: Option<f64>,
256
}
257

258
impl From<&NumberParam> for NumberParamDbType {
259
    fn from(value: &NumberParam) -> Self {
31✔
260
        match value {
31✔
261
            NumberParam::Static { value } => Self {
30✔
262
                value: Some(*value as i64),
30✔
263
                attribute: None,
30✔
264
                factor: None,
30✔
265
                default_value: None,
30✔
266
            },
30✔
267
            NumberParam::Derived(DerivedNumber {
268
                attribute,
1✔
269
                factor,
1✔
270
                default_value,
1✔
271
            }) => Self {
1✔
272
                value: None,
1✔
273
                attribute: Some(attribute.clone()),
1✔
274
                factor: Some(*factor),
1✔
275
                default_value: Some(*default_value),
1✔
276
            },
1✔
277
        }
278
    }
31✔
279
}
280

281
impl TryFrom<NumberParamDbType> for NumberParam {
282
    type Error = Error;
283

284
    fn try_from(value: NumberParamDbType) -> Result<Self, Self::Error> {
285
        match value {
1✔
286
            NumberParamDbType {
287
                value: Some(value),
24✔
288
                attribute: None,
24✔
289
                factor: None,
24✔
290
                default_value: None,
24✔
291
            } => Ok(Self::Static {
24✔
292
                value: value as usize,
24✔
293
            }),
24✔
294
            NumberParamDbType {
295
                value: None,
296
                attribute: Some(attribute),
1✔
297
                factor: Some(factor),
1✔
298
                default_value: Some(default_value),
1✔
299
            } => Ok(Self::Derived(DerivedNumber {
1✔
300
                attribute,
1✔
301
                factor,
1✔
302
                default_value,
1✔
303
            })),
1✔
304
            _ => Err(Error::UnexpectedInvalidDbTypeConversion),
×
305
        }
306
    }
25✔
307
}
308

309
#[derive(Debug, ToSql, FromSql)]
1,025✔
310
#[postgres(name = "Symbology")]
311
pub struct SymbologyDbType {
312
    raster: Option<RasterSymbology>,
313
    point: Option<PointSymbology>,
314
    line: Option<LineSymbology>,
315
    polygon: Option<PolygonSymbology>,
316
}
317

318
impl From<&Symbology> for SymbologyDbType {
319
    fn from(symbology: &Symbology) -> Self {
33✔
320
        match symbology {
33✔
321
            Symbology::Raster(raster) => SymbologyDbType {
20✔
322
                raster: Some(raster.clone()),
20✔
323
                point: None,
20✔
324
                line: None,
20✔
325
                polygon: None,
20✔
326
            },
20✔
327
            Symbology::Point(point) => SymbologyDbType {
11✔
328
                raster: None,
11✔
329
                point: Some(point.clone()),
11✔
330
                line: None,
11✔
331
                polygon: None,
11✔
332
            },
11✔
333
            Symbology::Line(line) => SymbologyDbType {
1✔
334
                raster: None,
1✔
335
                point: None,
1✔
336
                line: Some(line.clone()),
1✔
337
                polygon: None,
1✔
338
            },
1✔
339
            Symbology::Polygon(polygon) => SymbologyDbType {
1✔
340
                raster: None,
1✔
341
                point: None,
1✔
342
                line: None,
1✔
343
                polygon: Some(polygon.clone()),
1✔
344
            },
1✔
345
        }
346
    }
33✔
347
}
348

349
impl TryFrom<SymbologyDbType> for Symbology {
350
    type Error = Error;
351

352
    fn try_from(symbology: SymbologyDbType) -> Result<Self, Self::Error> {
353
        match symbology {
2✔
354
            SymbologyDbType {
355
                raster: Some(raster),
20✔
356
                point: None,
20✔
357
                line: None,
20✔
358
                polygon: None,
20✔
359
            } => Ok(Self::Raster(raster)),
20✔
360
            SymbologyDbType {
361
                raster: None,
362
                point: Some(point),
8✔
363
                line: None,
8✔
364
                polygon: None,
8✔
365
            } => Ok(Self::Point(point)),
8✔
366
            SymbologyDbType {
367
                raster: None,
368
                point: None,
369
                line: Some(line),
1✔
370
                polygon: None,
1✔
371
            } => Ok(Self::Line(line)),
1✔
372
            SymbologyDbType {
373
                raster: None,
374
                point: None,
375
                line: None,
376
                polygon: Some(polygon),
1✔
377
            } => Ok(Self::Polygon(polygon)),
1✔
378
            _ => Err(Error::UnexpectedInvalidDbTypeConversion),
×
379
        }
380
    }
30✔
381
}
382

383
#[derive(Debug, ToSql, FromSql)]
3,730✔
384
#[postgres(name = "Measurement")]
385
pub struct MeasurementDbType {
386
    continuous: Option<ContinuousMeasurement>,
387
    classification: Option<ClassificationMeasurementDbType>,
388
}
389

390
#[derive(Debug, ToSql, FromSql)]
3,454✔
391
struct SmallintTextKeyValue {
392
    key: i16,
393
    value: String,
394
}
395

396
#[derive(Debug, ToSql, FromSql)]
3,386✔
397
#[postgres(name = "ClassificationMeasurement")]
398
pub struct ClassificationMeasurementDbType {
399
    measurement: String,
400
    classes: Vec<SmallintTextKeyValue>,
401
}
402

403
impl From<&Measurement> for MeasurementDbType {
404
    fn from(measurement: &Measurement) -> Self {
219✔
405
        match measurement {
219✔
406
            Measurement::Unitless => Self {
205✔
407
                continuous: None,
205✔
408
                classification: None,
205✔
409
            },
205✔
410
            Measurement::Continuous(measurement) => Self {
11✔
411
                continuous: Some(measurement.clone()),
11✔
412
                classification: None,
11✔
413
            },
11✔
414
            Measurement::Classification(measurement) => Self {
3✔
415
                continuous: None,
3✔
416
                classification: Some(ClassificationMeasurementDbType {
3✔
417
                    measurement: measurement.measurement.clone(),
3✔
418
                    classes: measurement
3✔
419
                        .classes
3✔
420
                        .iter()
3✔
421
                        .map(|(key, value)| SmallintTextKeyValue {
36✔
422
                            key: i16::from(*key),
36✔
423
                            value: value.clone(),
36✔
424
                        })
36✔
425
                        .collect(),
3✔
426
                }),
3✔
427
            },
3✔
428
        }
429
    }
219✔
430
}
431

432
impl TryFrom<MeasurementDbType> for Measurement {
433
    type Error = Error;
434

435
    fn try_from(measurement: MeasurementDbType) -> Result<Self, Self::Error> {
436
        match measurement {
88✔
437
            MeasurementDbType {
438
                continuous: None,
439
                classification: None,
440
            } => Ok(Self::Unitless),
76✔
441
            MeasurementDbType {
442
                continuous: Some(continuous),
10✔
443
                classification: None,
10✔
444
            } => Ok(Self::Continuous(continuous)),
10✔
445
            MeasurementDbType {
446
                continuous: None,
447
                classification: Some(classification),
2✔
448
            } => {
2✔
449
                let mut classes = HashMap::with_capacity(classification.classes.len());
2✔
450
                for SmallintTextKeyValue { key, value } in classification.classes {
21✔
451
                    classes.insert(
452
                        u8::try_from(key).map_err(|_| Error::UnexpectedInvalidDbTypeConversion)?,
19✔
453
                        value,
19✔
454
                    );
455
                }
456

457
                Ok(Self::Classification(ClassificationMeasurement {
2✔
458
                    measurement: classification.measurement,
2✔
459
                    classes,
2✔
460
                }))
2✔
461
            }
462
            _ => Err(Error::UnexpectedInvalidDbTypeConversion),
×
463
        }
464
    }
88✔
465
}
466

467
#[derive(Debug, FromSql, ToSql)]
1,652✔
468
#[postgres(name = "VectorColumnInfo")]
469
pub struct VectorColumnInfoDbType {
470
    pub column: String,
471
    pub data_type: FeatureDataType,
472
    pub measurement: Measurement,
473
}
474

475
#[derive(Debug, FromSql, ToSql)]
2,478✔
476
#[postgres(name = "VectorResultDescriptor")]
477
pub struct VectorResultDescriptorDbType {
478
    pub data_type: VectorDataType,
479
    pub spatial_reference: SpatialReferenceOption,
480
    pub columns: Vec<VectorColumnInfoDbType>,
481
    pub time: Option<TimeInterval>,
482
    pub bbox: Option<BoundingBox2D>,
483
}
484

485
impl From<&VectorResultDescriptor> for VectorResultDescriptorDbType {
486
    fn from(result_descriptor: &VectorResultDescriptor) -> Self {
56✔
487
        Self {
56✔
488
            data_type: result_descriptor.data_type,
56✔
489
            spatial_reference: result_descriptor.spatial_reference,
56✔
490
            columns: result_descriptor
56✔
491
                .columns
56✔
492
                .iter()
56✔
493
                .map(|(column, info)| VectorColumnInfoDbType {
74✔
494
                    column: column.clone(),
74✔
495
                    data_type: info.data_type,
74✔
496
                    measurement: info.measurement.clone(),
74✔
497
                })
74✔
498
                .collect(),
56✔
499
            time: result_descriptor.time,
56✔
500
            bbox: result_descriptor.bbox,
56✔
501
        }
56✔
502
    }
56✔
503
}
504

505
impl TryFrom<VectorResultDescriptorDbType> for VectorResultDescriptor {
506
    type Error = Error;
507

508
    fn try_from(result_descriptor: VectorResultDescriptorDbType) -> Result<Self, Self::Error> {
32✔
509
        Ok(Self {
32✔
510
            data_type: result_descriptor.data_type,
32✔
511
            spatial_reference: result_descriptor.spatial_reference,
32✔
512
            columns: result_descriptor
32✔
513
                .columns
32✔
514
                .into_iter()
32✔
515
                .map(|info| {
39✔
516
                    (
39✔
517
                        info.column,
39✔
518
                        VectorColumnInfo {
39✔
519
                            data_type: info.data_type,
39✔
520
                            measurement: info.measurement,
39✔
521
                        },
39✔
522
                    )
39✔
523
                })
39✔
524
                .collect(),
32✔
525
            time: result_descriptor.time,
32✔
526
            bbox: result_descriptor.bbox,
32✔
527
        })
32✔
528
    }
32✔
529
}
530

531
#[derive(Debug, ToSql, FromSql)]
468✔
532
#[postgres(name = "ResultDescriptor")]
533
pub struct TypedResultDescriptorDbType {
534
    raster: Option<RasterResultDescriptor>,
535
    vector: Option<VectorResultDescriptor>,
536
    plot: Option<PlotResultDescriptor>,
537
}
538

539
impl From<&TypedResultDescriptor> for TypedResultDescriptorDbType {
540
    fn from(result_descriptor: &TypedResultDescriptor) -> Self {
94✔
541
        match result_descriptor {
94✔
542
            TypedResultDescriptor::Raster(raster) => Self {
67✔
543
                raster: Some(raster.clone()),
67✔
544
                vector: None,
67✔
545
                plot: None,
67✔
546
            },
67✔
547
            TypedResultDescriptor::Vector(vector) => Self {
26✔
548
                raster: None,
26✔
549
                vector: Some(vector.clone()),
26✔
550
                plot: None,
26✔
551
            },
26✔
552
            TypedResultDescriptor::Plot(plot) => Self {
1✔
553
                raster: None,
1✔
554
                vector: None,
1✔
555
                plot: Some(*plot),
1✔
556
            },
1✔
557
        }
558
    }
94✔
559
}
560

561
impl TryFrom<TypedResultDescriptorDbType> for TypedResultDescriptor {
562
    type Error = Error;
563

564
    fn try_from(result_descriptor: TypedResultDescriptorDbType) -> Result<Self, Self::Error> {
565
        match result_descriptor {
18✔
566
            TypedResultDescriptorDbType {
567
                raster: Some(raster),
5✔
568
                vector: None,
5✔
569
                plot: None,
5✔
570
            } => Ok(Self::Raster(raster)),
5✔
571
            TypedResultDescriptorDbType {
572
                raster: None,
573
                vector: Some(vector),
17✔
574
                plot: None,
17✔
575
            } => Ok(Self::Vector(vector)),
17✔
576
            TypedResultDescriptorDbType {
577
                raster: None,
578
                vector: None,
579
                plot: Some(plot),
1✔
580
            } => Ok(Self::Plot(plot)),
1✔
581
            _ => Err(Error::UnexpectedInvalidDbTypeConversion),
×
582
        }
583
    }
23✔
584
}
585

586
#[derive(Debug, PartialEq, ToSql, FromSql)]
600✔
587
pub struct TextTextKeyValue {
588
    key: String,
589
    value: String,
590
}
591

592
#[derive(Debug, PartialEq, ToSql, FromSql)]
187✔
593
#[postgres(transparent)]
594
pub struct HashMapTextTextDbType(pub Vec<TextTextKeyValue>);
595

596
impl From<&HashMap<String, String>> for HashMapTextTextDbType {
597
    fn from(map: &HashMap<String, String>) -> Self {
25✔
598
        Self(
25✔
599
            map.iter()
25✔
600
                .map(|(key, value)| TextTextKeyValue {
25✔
601
                    key: key.clone(),
12✔
602
                    value: value.clone(),
12✔
603
                })
25✔
604
                .collect(),
25✔
605
        )
25✔
606
    }
25✔
607
}
608

609
impl<S: std::hash::BuildHasher + std::default::Default> From<HashMapTextTextDbType>
610
    for HashMap<String, String, S>
611
{
612
    fn from(map: HashMapTextTextDbType) -> Self {
19✔
613
        map.0
19✔
614
            .into_iter()
19✔
615
            .map(|TextTextKeyValue { key, value }| (key, value))
19✔
616
            .collect()
19✔
617
    }
19✔
618
}
619

620
#[derive(Debug, ToSql, FromSql)]
1,958✔
621
#[postgres(name = "OgrSourceTimeFormat")]
622
pub struct OgrSourceTimeFormatDbType {
623
    custom: Option<OgrSourceTimeFormatCustomDbType>,
624
    unix_time_stamp: Option<OgrSourceTimeFormatUnixTimeStampDbType>,
625
}
626

627
#[derive(Debug, ToSql, FromSql)]
1,911✔
628
#[postgres(name = "OgrSourceTimeFormatCustom")]
629
pub struct OgrSourceTimeFormatCustomDbType {
630
    custom_format: DateTimeParseFormat,
631
}
632

633
#[derive(Debug, ToSql, FromSql)]
1,906✔
634
#[postgres(name = "OgrSourceTimeFormatUnixTimeStamp")]
635
pub struct OgrSourceTimeFormatUnixTimeStampDbType {
636
    timestamp_type: UnixTimeStampType,
637
    fmt: DateTimeParseFormat,
638
}
639

640
impl From<&OgrSourceTimeFormat> for OgrSourceTimeFormatDbType {
641
    fn from(other: &OgrSourceTimeFormat) -> Self {
21✔
642
        match other {
21✔
643
            OgrSourceTimeFormat::Custom { custom_format } => Self {
3✔
644
                custom: Some(OgrSourceTimeFormatCustomDbType {
3✔
645
                    custom_format: custom_format.clone(),
3✔
646
                }),
3✔
647
                unix_time_stamp: None,
3✔
648
            },
3✔
649
            OgrSourceTimeFormat::UnixTimeStamp {
650
                timestamp_type,
1✔
651
                fmt,
1✔
652
            } => Self {
1✔
653
                custom: None,
1✔
654
                unix_time_stamp: Some(OgrSourceTimeFormatUnixTimeStampDbType {
1✔
655
                    timestamp_type: *timestamp_type,
1✔
656
                    fmt: fmt.clone(),
1✔
657
                }),
1✔
658
            },
1✔
659
            OgrSourceTimeFormat::Auto => Self {
17✔
660
                custom: None,
17✔
661
                unix_time_stamp: None,
17✔
662
            },
17✔
663
        }
664
    }
21✔
665
}
666

667
impl TryFrom<OgrSourceTimeFormatDbType> for OgrSourceTimeFormat {
668
    type Error = Error;
669

670
    fn try_from(other: OgrSourceTimeFormatDbType) -> Result<Self, Self::Error> {
671
        match other {
14✔
672
            OgrSourceTimeFormatDbType {
673
                custom: Some(custom),
3✔
674
                unix_time_stamp: None,
3✔
675
            } => Ok(Self::Custom {
3✔
676
                custom_format: custom.custom_format,
3✔
677
            }),
3✔
678
            OgrSourceTimeFormatDbType {
679
                custom: None,
680
                unix_time_stamp: Some(unix_time_stamp),
1✔
681
            } => Ok(Self::UnixTimeStamp {
1✔
682
                timestamp_type: unix_time_stamp.timestamp_type,
1✔
683
                fmt: unix_time_stamp.fmt,
1✔
684
            }),
1✔
685
            OgrSourceTimeFormatDbType {
686
                custom: None,
687
                unix_time_stamp: None,
688
            } => Ok(Self::Auto),
10✔
689
            _ => Err(Error::UnexpectedInvalidDbTypeConversion),
×
690
        }
691
    }
14✔
692
}
693

694
#[derive(Debug, ToSql, FromSql)]
652✔
695
#[postgres(name = "OgrSourceDurationSpec")]
696
pub struct OgrSourceDurationSpecDbType {
697
    infinite: bool,
698
    zero: bool,
699
    value: Option<TimeStep>,
700
}
701

702
impl From<&OgrSourceDurationSpec> for OgrSourceDurationSpecDbType {
703
    fn from(other: &OgrSourceDurationSpec) -> Self {
16✔
704
        match other {
16✔
705
            OgrSourceDurationSpec::Infinite => Self {
1✔
706
                infinite: true,
1✔
707
                zero: false,
1✔
708
                value: None,
1✔
709
            },
1✔
710
            OgrSourceDurationSpec::Zero => Self {
14✔
711
                infinite: false,
14✔
712
                zero: true,
14✔
713
                value: None,
14✔
714
            },
14✔
715
            OgrSourceDurationSpec::Value(value) => Self {
1✔
716
                infinite: false,
1✔
717
                zero: false,
1✔
718
                value: Some(*value),
1✔
719
            },
1✔
720
        }
721
    }
16✔
722
}
723

724
impl TryFrom<OgrSourceDurationSpecDbType> for OgrSourceDurationSpec {
725
    type Error = Error;
726

727
    fn try_from(other: OgrSourceDurationSpecDbType) -> Result<Self, Self::Error> {
728
        match other {
9✔
729
            OgrSourceDurationSpecDbType {
730
                infinite: true,
731
                zero: false,
732
                value: None,
733
            } => Ok(Self::Infinite),
1✔
734
            OgrSourceDurationSpecDbType {
735
                infinite: false,
736
                zero: true,
737
                value: None,
738
            } => Ok(Self::Zero),
7✔
739
            OgrSourceDurationSpecDbType {
740
                infinite: false,
741
                zero: false,
742
                value: Some(value),
1✔
743
            } => Ok(Self::Value(value)),
1✔
744
            _ => Err(Error::UnexpectedInvalidDbTypeConversion),
×
745
        }
746
    }
9✔
747
}
748

749
#[derive(Debug, ToSql, FromSql)]
628✔
750
#[postgres(name = "OgrSourceDatasetTimeType")]
751
pub struct OgrSourceDatasetTimeTypeDbType {
752
    start: Option<OgrSourceDatasetTimeTypeStartDbType>,
753
    start_end: Option<OgrSourceDatasetTimeTypeStartEndDbType>,
754
    start_duration: Option<OgrSourceDatasetTimeTypeStartDurationDbType>,
755
}
756

757
#[derive(Debug, ToSql, FromSql)]
628✔
758
#[postgres(name = "OgrSourceDatasetTimeTypeStart")]
759
pub struct OgrSourceDatasetTimeTypeStartDbType {
760
    start_field: String,
761
    start_format: OgrSourceTimeFormat,
762
    duration: OgrSourceDurationSpec,
763
}
764

765
#[derive(Debug, ToSql, FromSql)]
785✔
766
#[postgres(name = "OgrSourceDatasetTimeTypeStartEnd")]
767
pub struct OgrSourceDatasetTimeTypeStartEndDbType {
768
    start_field: String,
769
    start_format: OgrSourceTimeFormat,
770
    end_field: String,
771
    end_format: OgrSourceTimeFormat,
772
}
773

774
#[derive(Debug, ToSql, FromSql)]
628✔
775
#[postgres(name = "OgrSourceDatasetTimeTypeStartDuration")]
776
pub struct OgrSourceDatasetTimeTypeStartDurationDbType {
777
    start_field: String,
778
    start_format: OgrSourceTimeFormat,
779
    duration_field: String,
780
}
781

782
impl From<&OgrSourceDatasetTimeType> for OgrSourceDatasetTimeTypeDbType {
783
    fn from(other: &OgrSourceDatasetTimeType) -> Self {
32✔
784
        match other {
32✔
785
            OgrSourceDatasetTimeType::None => Self {
16✔
786
                start: None,
16✔
787
                start_end: None,
16✔
788
                start_duration: None,
16✔
789
            },
16✔
790
            OgrSourceDatasetTimeType::Start {
791
                start_field,
13✔
792
                start_format,
13✔
793
                duration,
13✔
794
            } => Self {
13✔
795
                start: Some(OgrSourceDatasetTimeTypeStartDbType {
13✔
796
                    start_field: start_field.clone(),
13✔
797
                    start_format: start_format.clone(),
13✔
798
                    duration: *duration,
13✔
799
                }),
13✔
800
                start_end: None,
13✔
801
                start_duration: None,
13✔
802
            },
13✔
803
            OgrSourceDatasetTimeType::StartEnd {
804
                start_field,
2✔
805
                start_format,
2✔
806
                end_field,
2✔
807
                end_format,
2✔
808
            } => Self {
2✔
809
                start: None,
2✔
810
                start_end: Some(OgrSourceDatasetTimeTypeStartEndDbType {
2✔
811
                    start_field: start_field.clone(),
2✔
812
                    start_format: start_format.clone(),
2✔
813
                    end_field: end_field.clone(),
2✔
814
                    end_format: end_format.clone(),
2✔
815
                }),
2✔
816
                start_duration: None,
2✔
817
            },
2✔
818
            OgrSourceDatasetTimeType::StartDuration {
819
                start_field,
1✔
820
                start_format,
1✔
821
                duration_field,
1✔
822
            } => Self {
1✔
823
                start: None,
1✔
824
                start_end: None,
1✔
825
                start_duration: Some(OgrSourceDatasetTimeTypeStartDurationDbType {
1✔
826
                    start_field: start_field.clone(),
1✔
827
                    start_format: start_format.clone(),
1✔
828
                    duration_field: duration_field.clone(),
1✔
829
                }),
1✔
830
            },
1✔
831
        }
832
    }
32✔
833
}
834

835
impl TryFrom<OgrSourceDatasetTimeTypeDbType> for OgrSourceDatasetTimeType {
836
    type Error = Error;
837

838
    fn try_from(other: OgrSourceDatasetTimeTypeDbType) -> Result<Self, Self::Error> {
839
        match other {
11✔
840
            OgrSourceDatasetTimeTypeDbType {
841
                start: None,
842
                start_end: None,
843
                start_duration: None,
844
            } => Ok(Self::None),
8✔
845
            OgrSourceDatasetTimeTypeDbType {
846
                start: Some(start),
6✔
847
                start_end: None,
6✔
848
                start_duration: None,
6✔
849
            } => Ok(Self::Start {
6✔
850
                start_field: start.start_field,
6✔
851
                start_format: start.start_format,
6✔
852
                duration: start.duration,
6✔
853
            }),
6✔
854
            OgrSourceDatasetTimeTypeDbType {
855
                start: None,
856
                start_end: Some(start_end),
2✔
857
                start_duration: None,
2✔
858
            } => Ok(Self::StartEnd {
2✔
859
                start_field: start_end.start_field,
2✔
860
                start_format: start_end.start_format,
2✔
861
                end_field: start_end.end_field,
2✔
862
                end_format: start_end.end_format,
2✔
863
            }),
2✔
864
            OgrSourceDatasetTimeTypeDbType {
865
                start: None,
866
                start_end: None,
867
                start_duration: Some(start_duration),
1✔
868
            } => Ok(Self::StartDuration {
1✔
869
                start_field: start_duration.start_field,
1✔
870
                start_format: start_duration.start_format,
1✔
871
                duration_field: start_duration.duration_field,
1✔
872
            }),
1✔
873
            _ => Err(Error::UnexpectedInvalidDbTypeConversion),
×
874
        }
875
    }
17✔
876
}
877

878
#[derive(Debug, ToSql, FromSql)]
480✔
879
#[postgres(name = "FormatSpecifics")]
880
pub struct FormatSpecificsDbType {
881
    csv: Option<FormatSpecificsCsvDbType>,
882
}
883

884
#[derive(Debug, ToSql, FromSql)]
480✔
885
#[postgres(name = "FormatSpecificsCsv")]
886
pub struct FormatSpecificsCsvDbType {
887
    header: CsvHeader,
888
}
889

890
impl From<&FormatSpecifics> for FormatSpecificsDbType {
891
    fn from(other: &FormatSpecifics) -> Self {
14✔
892
        match other {
14✔
893
            FormatSpecifics::Csv { header } => Self {
14✔
894
                csv: Some(FormatSpecificsCsvDbType { header: *header }),
14✔
895
            },
14✔
896
        }
14✔
897
    }
14✔
898
}
899

900
impl TryFrom<FormatSpecificsDbType> for FormatSpecifics {
901
    type Error = Error;
902

903
    fn try_from(other: FormatSpecificsDbType) -> Result<Self, Self::Error> {
7✔
904
        match other {
7✔
905
            FormatSpecificsDbType {
906
                csv: Some(FormatSpecificsCsvDbType { header }),
7✔
907
            } => Ok(Self::Csv { header }),
7✔
908
            _ => Err(Error::UnexpectedInvalidDbTypeConversion),
×
909
        }
910
    }
7✔
911
}
912

913
#[derive(Debug, ToSql, FromSql)]
1,510✔
914
#[postgres(name = "OgrSourceColumnSpec")]
915
pub struct OgrSourceColumnSpecDbType {
916
    pub format_specifics: Option<FormatSpecifics>,
917
    pub x: String,
918
    pub y: Option<String>,
919
    pub int: Vec<String>,
920
    pub float: Vec<String>,
921
    pub text: Vec<String>,
922
    pub bool: Vec<String>,
923
    pub datetime: Vec<String>,
924
    pub rename: Option<HashMapTextTextDbType>,
925
}
926

927
impl From<&OgrSourceColumnSpec> for OgrSourceColumnSpecDbType {
928
    fn from(other: &OgrSourceColumnSpec) -> Self {
18✔
929
        Self {
18✔
930
            format_specifics: other.format_specifics.clone(),
18✔
931
            x: other.x.clone(),
18✔
932
            y: other.y.clone(),
18✔
933
            int: other.int.clone(),
18✔
934
            float: other.float.clone(),
18✔
935
            text: other.text.clone(),
18✔
936
            bool: other.bool.clone(),
18✔
937
            datetime: other.datetime.clone(),
18✔
938
            rename: other.rename.as_ref().map(Into::into),
18✔
939
        }
18✔
940
    }
18✔
941
}
942

943
impl TryFrom<OgrSourceColumnSpecDbType> for OgrSourceColumnSpec {
944
    type Error = Error;
945

946
    fn try_from(other: OgrSourceColumnSpecDbType) -> Result<Self, Self::Error> {
9✔
947
        Ok(Self {
9✔
948
            format_specifics: other.format_specifics,
9✔
949
            x: other.x,
9✔
950
            y: other.y,
9✔
951
            int: other.int,
9✔
952
            float: other.float,
9✔
953
            text: other.text,
9✔
954
            bool: other.bool,
9✔
955
            datetime: other.datetime,
9✔
956
            rename: other.rename.map(Into::into),
9✔
957
        })
9✔
958
    }
9✔
959
}
960

961
#[derive(Debug)]
×
962
pub struct PolygonRef<'p> {
963
    pub rings: &'p [Vec<Coordinate2D>],
964
}
965

966
#[derive(Debug)]
×
967
pub struct PolygonOwned {
968
    pub rings: Vec<Vec<Coordinate2D>>,
969
}
970

971
impl ToSql for PolygonRef<'_> {
972
    fn to_sql(
4✔
973
        &self,
4✔
974
        ty: &postgres_types::Type,
4✔
975
        w: &mut bytes::BytesMut,
4✔
976
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
4✔
977
        let postgres_types::Kind::Domain(domain_type) = ty.kind() else {
4✔
978
            panic!("expected domain type");
×
979
        };
980

981
        let postgres_types::Kind::Array(member_type) = domain_type.kind() else {
4✔
982
             panic!("expected array type");
×
983
        };
984

985
        let dimension = postgres_protocol::types::ArrayDimension {
4✔
986
            len: self.rings.len() as i32,
4✔
987
            lower_bound: 1, // arrays are one-indexed
4✔
988
        };
4✔
989

4✔
990
        postgres_protocol::types::array_to_sql(
4✔
991
            Some(dimension),
4✔
992
            member_type.oid(),
4✔
993
            self.rings.iter(),
4✔
994
            |coordinates, w| {
4✔
995
                postgres_protocol::types::path_to_sql(
8✔
996
                    true,
8✔
997
                    coordinates.iter().map(|p| (p.x, p.y)),
32✔
998
                    w,
8✔
999
                )?;
8✔
1000

1001
                Ok(postgres_protocol::IsNull::No)
8✔
1002
            },
8✔
1003
            w,
4✔
1004
        )?;
4✔
1005

1006
        Ok(postgres_types::IsNull::No)
4✔
1007
    }
4✔
1008

1009
    fn accepts(ty: &postgres_types::Type) -> bool {
1✔
1010
        if ty.name() != "Polygon" {
1✔
1011
            return false;
×
1012
        }
1✔
1013

1014
        let postgres_types::Kind::Domain(inner_type) = ty.kind() else {
1✔
1015
            return false;
×
1016
        };
1017

1018
        let postgres_types::Kind::Array(inner_type) = inner_type.kind() else {
1✔
1019
            return false;
×
1020
        };
1021

1022
        matches!(inner_type, &postgres_types::Type::PATH)
1✔
1023
    }
1✔
1024

1025
    postgres_types::to_sql_checked!();
1026
}
1027

1028
impl<'a> FromSql<'a> for PolygonOwned {
1029
    fn from_sql(
4✔
1030
        _ty: &postgres_types::Type,
4✔
1031
        raw: &'a [u8],
4✔
1032
    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
4✔
1033
        let array = postgres_protocol::types::array_from_sql(raw)?;
4✔
1034
        if array.dimensions().count()? > 1 {
4✔
1035
            return Err("array contains too many dimensions".into());
×
1036
        }
4✔
1037

1038
        let rings = array
4✔
1039
            .values()
4✔
1040
            .map(|raw| {
8✔
1041
                let Some(raw) = raw else {
8✔
1042
                    return Err("array contains NULL values".into());
×
1043
                };
1044
                let path = postgres_protocol::types::path_from_sql(raw)?;
8✔
1045

1046
                let coordinates = path
8✔
1047
                    .points()
8✔
1048
                    .map(|point| {
32✔
1049
                        Ok(Coordinate2D {
32✔
1050
                            x: point.x(),
32✔
1051
                            y: point.y(),
32✔
1052
                        })
32✔
1053
                    })
32✔
1054
                    .collect()?;
8✔
1055
                Ok(coordinates)
8✔
1056
            })
8✔
1057
            .collect()?;
4✔
1058

1059
        Ok(Self { rings })
4✔
1060
    }
4✔
1061

1062
    fn accepts(ty: &postgres_types::Type) -> bool {
×
1063
        if ty.name() != "Polygon" {
×
1064
            return false;
×
1065
        }
×
1066

1067
        let postgres_types::Kind::Domain(inner_type) = ty.kind() else {
×
1068
            return false;
×
1069
        };
1070

1071
        let postgres_types::Kind::Array(inner_type) = inner_type.kind() else {
×
1072
            return false;
×
1073
        };
1074

1075
        matches!(inner_type, &postgres_types::Type::PATH)
×
1076
    }
×
1077
}
1078

1079
#[derive(Debug, ToSql, FromSql)]
785✔
1080
#[postgres(name = "TypedGeometry")]
1081
pub struct TypedGeometryDbType {
1082
    data: bool,
1083
    multi_point: Option<MultiPoint>,
1084
    multi_line_string: Option<MultiLineString>,
1085
    multi_polygon: Option<MultiPolygon>,
1086
}
1087

1088
impl From<&TypedGeometry> for TypedGeometryDbType {
1089
    fn from(other: &TypedGeometry) -> Self {
7✔
1090
        match other {
7✔
1091
            TypedGeometry::Data(_) => Self {
1✔
1092
                data: true,
1✔
1093
                multi_point: None,
1✔
1094
                multi_line_string: None,
1✔
1095
                multi_polygon: None,
1✔
1096
            },
1✔
1097
            TypedGeometry::MultiPoint(points) => Self {
4✔
1098
                data: false,
4✔
1099
                multi_point: Some(points.clone()),
4✔
1100
                multi_line_string: None,
4✔
1101
                multi_polygon: None,
4✔
1102
            },
4✔
1103
            TypedGeometry::MultiLineString(lines) => Self {
1✔
1104
                data: false,
1✔
1105
                multi_point: None,
1✔
1106
                multi_line_string: Some(lines.clone()),
1✔
1107
                multi_polygon: None,
1✔
1108
            },
1✔
1109
            TypedGeometry::MultiPolygon(polygons) => Self {
1✔
1110
                data: false,
1✔
1111
                multi_point: None,
1✔
1112
                multi_line_string: None,
1✔
1113
                multi_polygon: Some(polygons.clone()),
1✔
1114
            },
1✔
1115
        }
1116
    }
7✔
1117
}
1118

1119
impl TryFrom<TypedGeometryDbType> for TypedGeometry {
1120
    type Error = Error;
1121

1122
    fn try_from(other: TypedGeometryDbType) -> Result<Self, Self::Error> {
1123
        match other {
2✔
1124
            TypedGeometryDbType {
1125
                data: true,
1126
                multi_point: None,
1127
                multi_line_string: None,
1128
                multi_polygon: None,
1129
            } => Ok(TypedGeometry::Data(NoGeometry)),
1✔
1130
            TypedGeometryDbType {
1131
                data: false,
1132
                multi_point: Some(multi_point),
4✔
1133
                multi_line_string: None,
4✔
1134
                multi_polygon: None,
4✔
1135
            } => Ok(TypedGeometry::MultiPoint(multi_point)),
4✔
1136
            TypedGeometryDbType {
1137
                data: false,
1138
                multi_point: None,
1139
                multi_line_string: Some(multi_line_string),
1✔
1140
                multi_polygon: None,
1✔
1141
            } => Ok(TypedGeometry::MultiLineString(multi_line_string)),
1✔
1142
            TypedGeometryDbType {
1143
                data: false,
1144
                multi_point: None,
1145
                multi_line_string: None,
1146
                multi_polygon: Some(multi_polygon),
1✔
1147
            } => Ok(TypedGeometry::MultiPolygon(multi_polygon)),
1✔
1148
            _ => Err(Error::UnexpectedInvalidDbTypeConversion),
×
1149
        }
1150
    }
7✔
1151
}
1152

1153
#[derive(Debug, ToSql, FromSql)]
1,937✔
1154
#[postgres(name = "OgrSourceDataset")]
1155
pub struct OgrSourceDatasetDbType {
1156
    pub file_name: String,
1157
    pub layer_name: String,
1158
    pub data_type: Option<VectorDataType>,
1159
    pub time: OgrSourceDatasetTimeType,
1160
    pub default_geometry: Option<TypedGeometry>,
1161
    pub columns: Option<OgrSourceColumnSpec>,
1162
    pub force_ogr_time_filter: bool,
1163
    pub force_ogr_spatial_filter: bool,
1164
    pub on_error: OgrSourceErrorSpec,
1165
    pub sql_query: Option<String>,
1166
    pub attribute_query: Option<String>,
1167
    pub cache_ttl: CacheTtlSeconds,
1168
}
1169

1170
impl From<&OgrSourceDataset> for OgrSourceDatasetDbType {
1171
    fn from(other: &OgrSourceDataset) -> Self {
28✔
1172
        Self {
28✔
1173
            file_name: other.file_name.to_string_lossy().to_string(),
28✔
1174
            layer_name: other.layer_name.clone(),
28✔
1175
            data_type: other.data_type,
28✔
1176
            time: other.time.clone(),
28✔
1177
            default_geometry: other.default_geometry.clone(),
28✔
1178
            columns: other.columns.clone(),
28✔
1179
            force_ogr_time_filter: other.force_ogr_time_filter,
28✔
1180
            force_ogr_spatial_filter: other.force_ogr_spatial_filter,
28✔
1181
            on_error: other.on_error,
28✔
1182
            sql_query: other.sql_query.clone(),
28✔
1183
            attribute_query: other.attribute_query.clone(),
28✔
1184
            cache_ttl: other.cache_ttl,
28✔
1185
        }
28✔
1186
    }
28✔
1187
}
1188

1189
impl TryFrom<OgrSourceDatasetDbType> for OgrSourceDataset {
1190
    type Error = Error;
1191

1192
    fn try_from(other: OgrSourceDatasetDbType) -> Result<Self, Self::Error> {
13✔
1193
        Ok(Self {
13✔
1194
            file_name: other.file_name.into(),
13✔
1195
            layer_name: other.layer_name,
13✔
1196
            data_type: other.data_type,
13✔
1197
            time: other.time,
13✔
1198
            default_geometry: other.default_geometry,
13✔
1199
            columns: other.columns,
13✔
1200
            force_ogr_time_filter: other.force_ogr_time_filter,
13✔
1201
            force_ogr_spatial_filter: other.force_ogr_spatial_filter,
13✔
1202
            on_error: other.on_error,
13✔
1203
            sql_query: other.sql_query,
13✔
1204
            attribute_query: other.attribute_query,
13✔
1205
            cache_ttl: other.cache_ttl,
13✔
1206
        })
13✔
1207
    }
13✔
1208
}
1209

1210
#[derive(Debug, ToSql, FromSql)]
489✔
1211
#[postgres(name = "OgrMetaData")]
1212
pub struct OgrMetaDataDbType {
1213
    pub loading_info: OgrSourceDataset,
1214
    pub result_descriptor: VectorResultDescriptor,
1215
}
1216

1217
impl From<&OgrMetaData> for OgrMetaDataDbType {
1218
    fn from(other: &OgrMetaData) -> Self {
27✔
1219
        Self {
27✔
1220
            loading_info: other.loading_info.clone(),
27✔
1221
            result_descriptor: other.result_descriptor.clone(),
27✔
1222
        }
27✔
1223
    }
27✔
1224
}
1225

1226
impl TryFrom<OgrMetaDataDbType> for OgrMetaData {
1227
    type Error = Error;
1228

1229
    fn try_from(other: OgrMetaDataDbType) -> Result<Self, Self::Error> {
12✔
1230
        Ok(Self {
12✔
1231
            loading_info: other.loading_info,
12✔
1232
            result_descriptor: other.result_descriptor,
12✔
1233
            phantom: PhantomData,
12✔
1234
        })
12✔
1235
    }
12✔
1236
}
1237

1238
#[derive(Debug, ToSql, FromSql)]
449✔
1239
#[postgres(name = "MockMetaData")]
1240
pub struct MockMetaDataDbType {
1241
    pub loading_info: MockDatasetDataSourceLoadingInfo,
1242
    pub result_descriptor: VectorResultDescriptor,
1243
}
1244

1245
impl From<&MockMetaData> for MockMetaDataDbType {
1246
    fn from(other: &MockMetaData) -> Self {
2✔
1247
        Self {
2✔
1248
            loading_info: other.loading_info.clone(),
2✔
1249
            result_descriptor: other.result_descriptor.clone(),
2✔
1250
        }
2✔
1251
    }
2✔
1252
}
1253

1254
impl TryFrom<MockMetaDataDbType> for MockMetaData {
1255
    type Error = Error;
1256

1257
    fn try_from(other: MockMetaDataDbType) -> Result<Self, Self::Error> {
2✔
1258
        Ok(Self {
2✔
1259
            loading_info: other.loading_info,
2✔
1260
            result_descriptor: other.result_descriptor,
2✔
1261
            phantom: PhantomData,
2✔
1262
        })
2✔
1263
    }
2✔
1264
}
1265

1266
#[derive(Debug, ToSql, FromSql)]
7,080✔
1267
#[postgres(name = "GdalDatasetParameters")]
1268
pub struct GdalDatasetParametersDbType {
1269
    pub file_path: String,
1270
    pub rasterband_channel: i64,
1271
    pub geo_transform: GdalDatasetGeoTransform,
1272
    pub width: i64,
1273
    pub height: i64,
1274
    pub file_not_found_handling: FileNotFoundHandling,
1275
    pub no_data_value: Option<f64>,
1276
    pub properties_mapping: Option<Vec<GdalMetadataMapping>>,
1277
    pub gdal_open_options: Option<Vec<String>>,
1278
    pub gdal_config_options: Option<Vec<GdalConfigOption>>,
1279
    pub allow_alphaband_as_mask: bool,
1280
}
1281

1282
impl From<&GdalDatasetParameters> for GdalDatasetParametersDbType {
1283
    fn from(other: &GdalDatasetParameters) -> Self {
75✔
1284
        Self {
75✔
1285
            file_path: other.file_path.to_string_lossy().to_string(),
75✔
1286
            rasterband_channel: other.rasterband_channel as i64,
75✔
1287
            geo_transform: other.geo_transform,
75✔
1288
            width: other.width as i64,
75✔
1289
            height: other.height as i64,
75✔
1290
            file_not_found_handling: other.file_not_found_handling,
75✔
1291
            no_data_value: other.no_data_value,
75✔
1292
            properties_mapping: other.properties_mapping.clone(),
75✔
1293
            gdal_open_options: other.gdal_open_options.clone(),
75✔
1294
            gdal_config_options: other.gdal_config_options.clone(),
75✔
1295
            allow_alphaband_as_mask: other.allow_alphaband_as_mask,
75✔
1296
        }
75✔
1297
    }
75✔
1298
}
1299

1300
impl TryFrom<GdalDatasetParametersDbType> for GdalDatasetParameters {
1301
    type Error = Error;
1302

1303
    fn try_from(other: GdalDatasetParametersDbType) -> Result<Self, Self::Error> {
41✔
1304
        Ok(Self {
41✔
1305
            file_path: other.file_path.into(),
41✔
1306
            rasterband_channel: other.rasterband_channel as usize,
41✔
1307
            geo_transform: other.geo_transform,
41✔
1308
            width: other.width as usize,
41✔
1309
            height: other.height as usize,
41✔
1310
            file_not_found_handling: other.file_not_found_handling,
41✔
1311
            no_data_value: other.no_data_value,
41✔
1312
            properties_mapping: other.properties_mapping,
41✔
1313
            gdal_open_options: other.gdal_open_options,
41✔
1314
            gdal_config_options: other.gdal_config_options,
41✔
1315
            allow_alphaband_as_mask: other.allow_alphaband_as_mask,
41✔
1316
        })
41✔
1317
    }
41✔
1318
}
1319

1320
#[derive(Debug, ToSql, FromSql, PartialEq)]
536✔
1321
pub struct TextGdalSourceTimePlaceholderKeyValue {
1322
    pub key: String,
1323
    pub value: GdalSourceTimePlaceholder,
1324
}
1325

1326
#[derive(Debug, ToSql, FromSql)]
1,029✔
1327
#[postgres(name = "GdalMetaDataRegular")]
1328
pub struct GdalMetaDataRegularDbType {
1329
    pub result_descriptor: RasterResultDescriptor,
1330
    pub params: GdalDatasetParameters,
1331
    pub time_placeholders: Vec<TextGdalSourceTimePlaceholderKeyValue>,
1332
    pub data_time: TimeInterval,
1333
    pub step: TimeStep,
1334
    pub cache_ttl: CacheTtlSeconds,
1335
}
1336

1337
impl From<&GdalMetaDataRegular> for GdalMetaDataRegularDbType {
1338
    fn from(other: &GdalMetaDataRegular) -> Self {
58✔
1339
        Self {
58✔
1340
            result_descriptor: other.result_descriptor.clone(),
58✔
1341
            params: other.params.clone(),
58✔
1342
            time_placeholders: other
58✔
1343
                .time_placeholders
58✔
1344
                .iter()
58✔
1345
                .map(|(key, value)| TextGdalSourceTimePlaceholderKeyValue {
58✔
1346
                    key: key.clone(),
56✔
1347
                    value: value.clone(),
56✔
1348
                })
58✔
1349
                .collect(),
58✔
1350
            data_time: other.data_time,
58✔
1351
            step: other.step,
58✔
1352
            cache_ttl: other.cache_ttl,
58✔
1353
        }
58✔
1354
    }
58✔
1355
}
1356

1357
impl TryFrom<GdalMetaDataRegularDbType> for GdalMetaDataRegular {
1358
    type Error = Error;
1359

1360
    fn try_from(other: GdalMetaDataRegularDbType) -> Result<Self, Self::Error> {
24✔
1361
        Ok(Self {
24✔
1362
            result_descriptor: other.result_descriptor,
24✔
1363
            params: other.params,
24✔
1364
            time_placeholders: other
24✔
1365
                .time_placeholders
24✔
1366
                .iter()
24✔
1367
                .map(|item| (item.key.clone(), item.value.clone()))
24✔
1368
                .collect(),
24✔
1369
            data_time: other.data_time,
24✔
1370
            step: other.step,
24✔
1371
            cache_ttl: other.cache_ttl,
24✔
1372
        })
24✔
1373
    }
24✔
1374
}
1375

1376
#[derive(Debug, ToSql, FromSql)]
1,176✔
1377
#[postgres(name = "GdalMetadataNetCdfCf")]
1378
pub struct GdalMetadataNetCdfCfDbType {
1379
    pub result_descriptor: RasterResultDescriptor,
1380
    pub params: GdalDatasetParameters,
1381
    pub start: TimeInstance,
1382
    pub end: TimeInstance,
1383
    pub step: TimeStep,
1384
    pub band_offset: i64,
1385
    pub cache_ttl: CacheTtlSeconds,
1386
}
1387

1388
impl From<&GdalMetadataNetCdfCf> for GdalMetadataNetCdfCfDbType {
1389
    fn from(other: &GdalMetadataNetCdfCf) -> Self {
4✔
1390
        Self {
4✔
1391
            result_descriptor: other.result_descriptor.clone(),
4✔
1392
            params: other.params.clone(),
4✔
1393
            start: other.start,
4✔
1394
            end: other.end,
4✔
1395
            step: other.step,
4✔
1396
            band_offset: other.band_offset as i64,
4✔
1397
            cache_ttl: other.cache_ttl,
4✔
1398
        }
4✔
1399
    }
4✔
1400
}
1401

1402
impl TryFrom<GdalMetadataNetCdfCfDbType> for GdalMetadataNetCdfCf {
1403
    type Error = Error;
1404

1405
    fn try_from(other: GdalMetadataNetCdfCfDbType) -> Result<Self, Self::Error> {
4✔
1406
        Ok(Self {
4✔
1407
            result_descriptor: other.result_descriptor,
4✔
1408
            params: other.params,
4✔
1409
            start: other.start,
4✔
1410
            end: other.end,
4✔
1411
            step: other.step,
4✔
1412
            band_offset: other.band_offset as usize,
4✔
1413
            cache_ttl: other.cache_ttl,
4✔
1414
        })
4✔
1415
    }
4✔
1416
}
1417

1418
#[derive(Debug, ToSql, FromSql)]
1,015✔
1419
#[postgres(name = "MetaDataDefinition")]
1420
pub struct MetaDataDefinitionDbType {
1421
    mock_meta_data: Option<MockMetaData>,
1422
    ogr_meta_data: Option<OgrMetaData>,
1423
    gdal_meta_data_regular: Option<GdalMetaDataRegular>,
1424
    gdal_static: Option<GdalMetaDataStatic>,
1425
    gdal_metadata_net_cdf_cf: Option<GdalMetadataNetCdfCf>,
1426
    gdal_meta_data_list: Option<GdalMetaDataList>,
1427
}
1428

1429
impl From<&MetaDataDefinition> for MetaDataDefinitionDbType {
1430
    fn from(other: &MetaDataDefinition) -> Self {
97✔
1431
        match other {
97✔
1432
            MetaDataDefinition::MockMetaData(meta_data) => Self {
1✔
1433
                mock_meta_data: Some(meta_data.clone().into()),
1✔
1434
                ogr_meta_data: None,
1✔
1435
                gdal_meta_data_regular: None,
1✔
1436
                gdal_static: None,
1✔
1437
                gdal_metadata_net_cdf_cf: None,
1✔
1438
                gdal_meta_data_list: None,
1✔
1439
            },
1✔
1440
            MetaDataDefinition::OgrMetaData(meta_data) => Self {
26✔
1441
                mock_meta_data: None,
26✔
1442
                ogr_meta_data: Some(meta_data.clone().into()),
26✔
1443
                gdal_meta_data_regular: None,
26✔
1444
                gdal_static: None,
26✔
1445
                gdal_metadata_net_cdf_cf: None,
26✔
1446
                gdal_meta_data_list: None,
26✔
1447
            },
26✔
1448
            MetaDataDefinition::GdalMetaDataRegular(meta_data) => Self {
57✔
1449
                mock_meta_data: None,
57✔
1450
                ogr_meta_data: None,
57✔
1451
                gdal_meta_data_regular: Some(meta_data.clone().into()),
57✔
1452
                gdal_static: None,
57✔
1453
                gdal_metadata_net_cdf_cf: None,
57✔
1454
                gdal_meta_data_list: None,
57✔
1455
            },
57✔
1456
            MetaDataDefinition::GdalStatic(meta_data) => Self {
5✔
1457
                mock_meta_data: None,
5✔
1458
                ogr_meta_data: None,
5✔
1459
                gdal_meta_data_regular: None,
5✔
1460
                gdal_static: Some(meta_data.clone().into()),
5✔
1461
                gdal_metadata_net_cdf_cf: None,
5✔
1462
                gdal_meta_data_list: None,
5✔
1463
            },
5✔
1464
            MetaDataDefinition::GdalMetadataNetCdfCf(meta_data) => Self {
3✔
1465
                mock_meta_data: None,
3✔
1466
                ogr_meta_data: None,
3✔
1467
                gdal_meta_data_regular: None,
3✔
1468
                gdal_static: None,
3✔
1469
                gdal_metadata_net_cdf_cf: Some(meta_data.clone().into()),
3✔
1470
                gdal_meta_data_list: None,
3✔
1471
            },
3✔
1472
            MetaDataDefinition::GdalMetaDataList(meta_data) => Self {
5✔
1473
                mock_meta_data: None,
5✔
1474
                ogr_meta_data: None,
5✔
1475
                gdal_meta_data_regular: None,
5✔
1476
                gdal_static: None,
5✔
1477
                gdal_metadata_net_cdf_cf: None,
5✔
1478
                gdal_meta_data_list: Some(meta_data.clone().into()),
5✔
1479
            },
5✔
1480
        }
1481
    }
97✔
1482
}
1483

1484
impl TryFrom<MetaDataDefinitionDbType> for MetaDataDefinition {
1485
    type Error = Error;
1486

1487
    fn try_from(other: MetaDataDefinitionDbType) -> Result<Self, Self::Error> {
1488
        match other {
8✔
1489
            MetaDataDefinitionDbType {
1490
                mock_meta_data: Some(meta_data),
1✔
1491
                ogr_meta_data: None,
1✔
1492
                gdal_meta_data_regular: None,
1✔
1493
                gdal_static: None,
1✔
1494
                gdal_metadata_net_cdf_cf: None,
1✔
1495
                gdal_meta_data_list: None,
1✔
1496
            } => Ok(MetaDataDefinition::MockMetaData(meta_data.into())),
1✔
1497
            MetaDataDefinitionDbType {
1498
                mock_meta_data: None,
1499
                ogr_meta_data: Some(meta_data),
11✔
1500
                gdal_meta_data_regular: None,
11✔
1501
                gdal_static: None,
11✔
1502
                gdal_metadata_net_cdf_cf: None,
11✔
1503
                gdal_meta_data_list: None,
11✔
1504
            } => Ok(MetaDataDefinition::OgrMetaData(meta_data.into())),
11✔
1505
            MetaDataDefinitionDbType {
1506
                mock_meta_data: None,
1507
                ogr_meta_data: None,
1508
                gdal_meta_data_regular: Some(meta_data),
23✔
1509
                gdal_static: None,
23✔
1510
                gdal_metadata_net_cdf_cf: None,
23✔
1511
                gdal_meta_data_list: None,
23✔
1512
            } => Ok(MetaDataDefinition::GdalMetaDataRegular(meta_data.into())),
23✔
1513
            MetaDataDefinitionDbType {
1514
                mock_meta_data: None,
1515
                ogr_meta_data: None,
1516
                gdal_meta_data_regular: None,
1517
                gdal_static: Some(meta_data),
5✔
1518
                gdal_metadata_net_cdf_cf: None,
5✔
1519
                gdal_meta_data_list: None,
5✔
1520
            } => Ok(MetaDataDefinition::GdalStatic(meta_data.into())),
5✔
1521
            MetaDataDefinitionDbType {
1522
                mock_meta_data: None,
1523
                ogr_meta_data: None,
1524
                gdal_meta_data_regular: None,
1525
                gdal_static: None,
1526
                gdal_metadata_net_cdf_cf: Some(meta_data),
3✔
1527
                gdal_meta_data_list: None,
3✔
1528
            } => Ok(MetaDataDefinition::GdalMetadataNetCdfCf(meta_data.into())),
3✔
1529
            MetaDataDefinitionDbType {
1530
                mock_meta_data: None,
1531
                ogr_meta_data: None,
1532
                gdal_meta_data_regular: None,
1533
                gdal_static: None,
1534
                gdal_metadata_net_cdf_cf: None,
1535
                gdal_meta_data_list: Some(meta_data),
5✔
1536
            } => Ok(MetaDataDefinition::GdalMetaDataList(meta_data.into())),
5✔
1537
            _ => Err(Error::UnexpectedInvalidDbTypeConversion),
×
1538
        }
1539
    }
48✔
1540
}
1541

1542
/// A macro for quickly implementing `FromSql` and `ToSql` for `$RustType` if there is a `From` and `Into`
1543
/// implementation for another type `$DbType` that already implements it.
1544
///
1545
/// # Usage
1546
///
1547
/// ```rust,ignore
1548
/// delegate_from_to_sql!($RustType, $DbType)
1549
/// ```
1550
///
1551
macro_rules! delegate_from_to_sql {
1552
    ( $RustType:ty, $DbType:ty ) => {
1553
        impl ToSql for $RustType {
1554
            fn to_sql(
895✔
1555
                &self,
895✔
1556
                ty: &postgres_types::Type,
895✔
1557
                w: &mut bytes::BytesMut,
895✔
1558
            ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
895✔
1559
                <$DbType as ToSql>::to_sql(&self.into(), ty, w)
895✔
1560
            }
895✔
1561

1562
            fn accepts(ty: &postgres_types::Type) -> bool {
378✔
1563
                <$DbType as FromSql>::accepts(ty)
378✔
1564
            }
378✔
1565

1566
            postgres_types::to_sql_checked!();
1567
        }
1568

1569
        impl<'a> FromSql<'a> for $RustType {
1570
            fn from_sql(
462✔
1571
                ty: &postgres_types::Type,
462✔
1572
                raw: &'a [u8],
462✔
1573
            ) -> Result<$RustType, Box<dyn std::error::Error + Sync + Send>> {
462✔
1574
                Ok(<$DbType as FromSql>::from_sql(ty, raw)?.try_into()?)
462✔
1575
            }
462✔
1576

1577
            fn accepts(ty: &postgres_types::Type) -> bool {
10,560✔
1578
                <$DbType as FromSql>::accepts(ty)
10,560✔
1579
            }
10,560✔
1580
        }
1581
    };
1582
}
1583

1584
delegate_from_to_sql!(Colorizer, ColorizerDbType);
1585
delegate_from_to_sql!(ColorParam, ColorParamDbType);
1586
delegate_from_to_sql!(DefaultColors, DefaultColorsDbType);
1587
delegate_from_to_sql!(FormatSpecifics, FormatSpecificsDbType);
1588
delegate_from_to_sql!(GdalDatasetParameters, GdalDatasetParametersDbType);
1589
delegate_from_to_sql!(GdalMetadataNetCdfCf, GdalMetadataNetCdfCfDbType);
1590
delegate_from_to_sql!(GdalMetaDataRegular, GdalMetaDataRegularDbType);
1591
delegate_from_to_sql!(Measurement, MeasurementDbType);
1592
delegate_from_to_sql!(MetaDataDefinition, MetaDataDefinitionDbType);
1593
delegate_from_to_sql!(MockMetaData, MockMetaDataDbType);
1594
delegate_from_to_sql!(NumberParam, NumberParamDbType);
1595
delegate_from_to_sql!(OgrMetaData, OgrMetaDataDbType);
1596
delegate_from_to_sql!(OgrSourceColumnSpec, OgrSourceColumnSpecDbType);
1597
delegate_from_to_sql!(OgrSourceDataset, OgrSourceDatasetDbType);
1598
delegate_from_to_sql!(OgrSourceDatasetTimeType, OgrSourceDatasetTimeTypeDbType);
1599
delegate_from_to_sql!(OgrSourceDurationSpec, OgrSourceDurationSpecDbType);
1600
delegate_from_to_sql!(OgrSourceTimeFormat, OgrSourceTimeFormatDbType);
1601
delegate_from_to_sql!(Symbology, SymbologyDbType);
1602
delegate_from_to_sql!(TypedGeometry, TypedGeometryDbType);
1603
delegate_from_to_sql!(TypedResultDescriptor, TypedResultDescriptorDbType);
1604
delegate_from_to_sql!(VectorResultDescriptor, VectorResultDescriptorDbType);
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

© 2026 Coveralls, Inc