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

geo-engine / geoengine / 6708969456

31 Oct 2023 03:43PM UTC coverage: 89.489% (-0.009%) from 89.498%
6708969456

push

github

web-flow
Merge pull request #882 from geo-engine/API-docs

Api docs

493 of 493 new or added lines in 15 files covered. (100.0%)

109483 of 122343 relevant lines covered (89.49%)

59359.8 hits per line

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

41.49
/services/src/api/model/datatypes.rs
1
use crate::error::{self, Result};
2
use crate::identifier;
3
use geoengine_datatypes::primitives::{
4
    AxisAlignedRectangle, MultiLineStringAccess, MultiPointAccess, MultiPolygonAccess,
5
};
6
use ordered_float::NotNan;
7
use postgres_types::{FromSql, ToSql};
8
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
9
use snafu::ResultExt;
10
use std::{
11
    collections::{BTreeMap, HashMap},
12
    fmt::{Debug, Formatter},
13
    str::FromStr,
14
};
15
use utoipa::ToSchema;
16

17
identifier!(DataProviderId);
×
18

19
impl From<DataProviderId> for geoengine_datatypes::dataset::DataProviderId {
20
    fn from(value: DataProviderId) -> Self {
5✔
21
        Self(value.0)
5✔
22
    }
5✔
23
}
24

25
// Identifier for datasets managed by Geo Engine
26
identifier!(DatasetId);
×
27

28
impl From<DatasetId> for geoengine_datatypes::dataset::DatasetId {
29
    fn from(value: DatasetId) -> Self {
×
30
        Self(value.0)
×
31
    }
×
32
}
33

34
#[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
2✔
35
#[serde(rename_all = "camelCase", tag = "type")]
36
/// The identifier for loadable data. It is used in the source operators to get the loading info (aka parametrization)
37
/// for accessing the data. Internal data is loaded from datasets, external from `DataProvider`s.
38
pub enum DataId {
39
    #[serde(rename_all = "camelCase")]
40
    Internal {
41
        dataset_id: DatasetId,
42
    },
43
    External(ExternalDataId),
44
}
45

46
impl DataId {
47
    pub fn internal(&self) -> Option<DatasetId> {
48
        if let Self::Internal {
49
            dataset_id: dataset,
×
50
        } = self
×
51
        {
52
            return Some(*dataset);
×
53
        }
×
54
        None
×
55
    }
×
56

57
    pub fn external(&self) -> Option<ExternalDataId> {
58
        if let Self::External(id) = self {
×
59
            return Some(id.clone());
×
60
        }
×
61
        None
×
62
    }
×
63
}
64

65
impl From<DatasetId> for DataId {
66
    fn from(value: DatasetId) -> Self {
2✔
67
        Self::Internal { dataset_id: value }
2✔
68
    }
2✔
69
}
70

71
impl From<ExternalDataId> for DataId {
72
    fn from(value: ExternalDataId) -> Self {
×
73
        Self::External(value)
×
74
    }
×
75
}
76

77
impl From<ExternalDataId> for geoengine_datatypes::dataset::DataId {
78
    fn from(value: ExternalDataId) -> Self {
×
79
        Self::External(value.into())
×
80
    }
×
81
}
82

83
impl From<DatasetId> for geoengine_datatypes::dataset::DataId {
84
    fn from(value: DatasetId) -> Self {
×
85
        Self::Internal {
×
86
            dataset_id: value.into(),
×
87
        }
×
88
    }
×
89
}
90

91
impl From<ExternalDataId> for geoengine_datatypes::dataset::ExternalDataId {
92
    fn from(value: ExternalDataId) -> Self {
×
93
        Self {
×
94
            provider_id: value.provider_id.into(),
×
95
            layer_id: value.layer_id.into(),
×
96
        }
×
97
    }
×
98
}
99

100
impl From<geoengine_datatypes::dataset::DataId> for DataId {
101
    fn from(id: geoengine_datatypes::dataset::DataId) -> Self {
4✔
102
        match id {
4✔
103
            geoengine_datatypes::dataset::DataId::Internal { dataset_id } => Self::Internal {
4✔
104
                dataset_id: dataset_id.into(),
4✔
105
            },
4✔
106
            geoengine_datatypes::dataset::DataId::External(external_id) => {
×
107
                Self::External(external_id.into())
×
108
            }
109
        }
110
    }
4✔
111
}
112

113
impl From<DataId> for geoengine_datatypes::dataset::DataId {
114
    fn from(id: DataId) -> Self {
×
115
        match id {
×
116
            DataId::Internal { dataset_id } => Self::Internal {
×
117
                dataset_id: dataset_id.into(),
×
118
            },
×
119
            DataId::External(external_id) => Self::External(external_id.into()),
×
120
        }
121
    }
×
122
}
123

124
impl From<&DataId> for geoengine_datatypes::dataset::DataId {
125
    fn from(id: &DataId) -> Self {
×
126
        match id {
×
127
            DataId::Internal { dataset_id } => Self::Internal {
×
128
                dataset_id: dataset_id.into(),
×
129
            },
×
130
            DataId::External(external_id) => Self::External(external_id.into()),
×
131
        }
132
    }
×
133
}
134

135
impl From<geoengine_datatypes::dataset::DatasetId> for DatasetId {
136
    fn from(id: geoengine_datatypes::dataset::DatasetId) -> Self {
6✔
137
        Self(id.0)
6✔
138
    }
6✔
139
}
140

141
impl From<&DatasetId> for geoengine_datatypes::dataset::DatasetId {
142
    fn from(value: &DatasetId) -> Self {
2✔
143
        Self(value.0)
2✔
144
    }
2✔
145
}
146

147
impl From<&ExternalDataId> for geoengine_datatypes::dataset::ExternalDataId {
148
    fn from(value: &ExternalDataId) -> Self {
×
149
        Self {
×
150
            provider_id: value.provider_id.into(),
×
151
            layer_id: value.layer_id.clone().into(),
×
152
        }
×
153
    }
×
154
}
155

156
/// The user-facing identifier for loadable data.
157
/// It can be resolved into a [`DataId`].
158
#[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize)]
×
159
// TODO: Have separate type once `geoengine_datatypes::dataset::NamedData` is not part of the API anymore.
160
#[serde(
161
    from = "geoengine_datatypes::dataset::NamedData",
162
    into = "geoengine_datatypes::dataset::NamedData"
163
)]
164
pub struct NamedData {
165
    pub namespace: Option<String>,
166
    pub provider: Option<String>,
167
    pub name: String,
168
}
169

170
impl From<geoengine_datatypes::dataset::NamedData> for NamedData {
171
    fn from(
×
172
        geoengine_datatypes::dataset::NamedData {
×
173
            namespace,
×
174
            provider,
×
175
            name,
×
176
        }: geoengine_datatypes::dataset::NamedData,
×
177
    ) -> Self {
×
178
        Self {
×
179
            namespace,
×
180
            provider,
×
181
            name,
×
182
        }
×
183
    }
×
184
}
185

186
impl From<&geoengine_datatypes::dataset::NamedData> for NamedData {
187
    fn from(named_data: &geoengine_datatypes::dataset::NamedData) -> Self {
×
188
        Self::from(named_data.clone())
×
189
    }
×
190
}
191

192
impl From<NamedData> for geoengine_datatypes::dataset::NamedData {
193
    fn from(
1✔
194
        NamedData {
1✔
195
            namespace,
1✔
196
            provider,
1✔
197
            name,
1✔
198
        }: NamedData,
1✔
199
    ) -> Self {
1✔
200
        Self {
1✔
201
            namespace,
1✔
202
            provider,
1✔
203
            name,
1✔
204
        }
1✔
205
    }
1✔
206
}
207

208
impl From<&NamedData> for geoengine_datatypes::dataset::NamedData {
209
    fn from(named_data: &NamedData) -> Self {
×
210
        Self::from(named_data.clone())
×
211
    }
×
212
}
213

214
impl<'a> ToSchema<'a> for NamedData {
215
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
216
        use utoipa::openapi::*;
2✔
217
        (
2✔
218
            "NamedData",
2✔
219
            ObjectBuilder::new().schema_type(SchemaType::String).into(),
2✔
220
        )
2✔
221
    }
2✔
222
}
223

224
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, ToSchema, ToSql, FromSql)]
10✔
225
pub struct LayerId(pub String); // TODO: differentiate between internal layer ids (UUID) and external layer ids (String)
226

227
impl From<LayerId> for geoengine_datatypes::dataset::LayerId {
228
    fn from(value: LayerId) -> Self {
11✔
229
        Self(value.0)
11✔
230
    }
11✔
231
}
232

233
impl std::fmt::Display for LayerId {
234
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
×
235
        write!(f, "{}", self.0)
×
236
    }
×
237
}
238

239
#[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
2✔
240
#[serde(rename_all = "camelCase")]
241
pub struct ExternalDataId {
242
    pub provider_id: DataProviderId,
243
    pub layer_id: LayerId,
244
}
245

246
impl From<geoengine_datatypes::dataset::ExternalDataId> for ExternalDataId {
247
    fn from(id: geoengine_datatypes::dataset::ExternalDataId) -> Self {
×
248
        Self {
×
249
            provider_id: id.provider_id.into(),
×
250
            layer_id: id.layer_id.into(),
×
251
        }
×
252
    }
×
253
}
254

255
impl From<geoengine_datatypes::dataset::DataProviderId> for DataProviderId {
256
    fn from(id: geoengine_datatypes::dataset::DataProviderId) -> Self {
×
257
        Self(id.0)
×
258
    }
×
259
}
260

261
impl From<geoengine_datatypes::dataset::LayerId> for LayerId {
262
    fn from(id: geoengine_datatypes::dataset::LayerId) -> Self {
1✔
263
        Self(id.0)
1✔
264
    }
1✔
265
}
266

267
/// A spatial reference authority that is part of a spatial reference definition
268
#[derive(
269
    Debug,
×
270
    Copy,
271
    Clone,
×
272
    Eq,
273
    PartialEq,
24✔
274
    Ord,
×
275
    PartialOrd,
×
276
    Serialize,
×
277
    Deserialize,
×
278
    ToSchema,
2✔
279
    FromSql,
×
280
    ToSql,
×
281
)]
282
#[serde(rename_all = "SCREAMING-KEBAB-CASE")]
283
pub enum SpatialReferenceAuthority {
284
    Epsg,
285
    SrOrg,
286
    Iau2000,
287
    Esri,
288
}
289

290
impl From<geoengine_datatypes::spatial_reference::SpatialReferenceAuthority>
291
    for SpatialReferenceAuthority
292
{
293
    fn from(value: geoengine_datatypes::spatial_reference::SpatialReferenceAuthority) -> Self {
46✔
294
        match value {
46✔
295
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Epsg => Self::Epsg,
45✔
296
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::SrOrg => Self::SrOrg,
1✔
297
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Iau2000 => {
298
                Self::Iau2000
×
299
            }
300
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Esri => Self::Esri,
×
301
        }
302
    }
46✔
303
}
304

305
impl From<SpatialReferenceAuthority>
306
    for geoengine_datatypes::spatial_reference::SpatialReferenceAuthority
307
{
308
    fn from(value: SpatialReferenceAuthority) -> Self {
12✔
309
        match value {
12✔
310
            SpatialReferenceAuthority::Epsg => Self::Epsg,
12✔
311
            SpatialReferenceAuthority::SrOrg => Self::SrOrg,
×
312
            SpatialReferenceAuthority::Iau2000 => Self::Iau2000,
×
313
            SpatialReferenceAuthority::Esri => Self::Esri,
×
314
        }
315
    }
12✔
316
}
317

318
impl FromStr for SpatialReferenceAuthority {
319
    type Err = error::Error;
320

321
    fn from_str(s: &str) -> Result<Self, Self::Err> {
34✔
322
        Ok(match s {
34✔
323
            "EPSG" => SpatialReferenceAuthority::Epsg,
34✔
324
            "SR-ORG" => SpatialReferenceAuthority::SrOrg,
×
325
            "IAU2000" => SpatialReferenceAuthority::Iau2000,
×
326
            "ESRI" => SpatialReferenceAuthority::Esri,
×
327
            _ => {
328
                return Err(error::Error::InvalidSpatialReferenceString {
×
329
                    spatial_reference_string: s.into(),
×
330
                })
×
331
            }
332
        })
333
    }
34✔
334
}
335

336
impl std::fmt::Display for SpatialReferenceAuthority {
337
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42✔
338
        write!(
42✔
339
            f,
42✔
340
            "{}",
42✔
341
            match self {
42✔
342
                SpatialReferenceAuthority::Epsg => "EPSG",
42✔
343
                SpatialReferenceAuthority::SrOrg => "SR-ORG",
×
344
                SpatialReferenceAuthority::Iau2000 => "IAU2000",
×
345
                SpatialReferenceAuthority::Esri => "ESRI",
×
346
            }
347
        )
348
    }
42✔
349
}
350

351
/// A spatial reference consists of an authority and a code
352
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, FromSql, ToSql)]
24✔
353
pub struct SpatialReference {
354
    authority: SpatialReferenceAuthority,
355
    code: u32,
356
}
357

358
impl SpatialReference {
359
    pub fn proj_string(self) -> Result<String> {
360
        match self.authority {
×
361
            SpatialReferenceAuthority::Epsg | SpatialReferenceAuthority::Iau2000 => {
362
                Ok(format!("{}:{}", self.authority, self.code))
17✔
363
            }
364
            // poor-mans integration of Meteosat Second Generation
365
            SpatialReferenceAuthority::SrOrg if self.code == 81 => Ok("+proj=geos +lon_0=0 +h=35785831 +x_0=0 +y_0=0 +ellps=WGS84 +units=m +no_defs +type=crs".to_owned()),
×
366
            SpatialReferenceAuthority::SrOrg | SpatialReferenceAuthority::Esri => {
367
                Err(error::Error::ProjStringUnresolvable { spatial_ref: self })
×
368
                //TODO: we might need to look them up somehow! Best solution would be a registry where we can store user definexd srs strings.
369
            }
370
        }
371
    }
17✔
372

373
    /// Return the srs-string "authority:code"
374
    #[allow(clippy::trivially_copy_pass_by_ref)]
375
    pub fn srs_string(&self) -> String {
17✔
376
        format!("{}:{}", self.authority, self.code)
17✔
377
    }
17✔
378
}
379

380
impl From<geoengine_datatypes::spatial_reference::SpatialReference> for SpatialReference {
381
    fn from(value: geoengine_datatypes::spatial_reference::SpatialReference) -> Self {
46✔
382
        Self {
46✔
383
            authority: (*value.authority()).into(),
46✔
384
            code: value.code(),
46✔
385
        }
46✔
386
    }
46✔
387
}
388

389
impl From<SpatialReference> for geoengine_datatypes::spatial_reference::SpatialReference {
390
    fn from(value: SpatialReference) -> Self {
12✔
391
        geoengine_datatypes::spatial_reference::SpatialReference::new(
12✔
392
            value.authority.into(),
12✔
393
            value.code,
12✔
394
        )
12✔
395
    }
12✔
396
}
397

398
impl SpatialReference {
399
    pub fn new(authority: SpatialReferenceAuthority, code: u32) -> Self {
39✔
400
        Self { authority, code }
39✔
401
    }
39✔
402

403
    pub fn authority(&self) -> &SpatialReferenceAuthority {
1✔
404
        &self.authority
1✔
405
    }
1✔
406

407
    pub fn code(self) -> u32 {
1✔
408
        self.code
1✔
409
    }
1✔
410
}
411

412
impl FromStr for SpatialReference {
413
    type Err = error::Error;
414

415
    fn from_str(s: &str) -> Result<Self, Self::Err> {
34✔
416
        let mut split = s.split(':');
34✔
417

34✔
418
        match (split.next(), split.next(), split.next()) {
34✔
419
            (Some(authority), Some(code), None) => Ok(Self::new(
34✔
420
                authority.parse()?,
34✔
421
                code.parse::<u32>().context(error::ParseU32)?,
34✔
422
            )),
423
            _ => Err(error::Error::InvalidSpatialReferenceString {
×
424
                spatial_reference_string: s.into(),
×
425
            }),
×
426
        }
427
    }
34✔
428
}
429

430
impl std::fmt::Display for SpatialReference {
431
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
7✔
432
        write!(f, "{}:{}", self.authority, self.code)
7✔
433
    }
7✔
434
}
435

436
impl Serialize for SpatialReference {
437
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
1✔
438
    where
1✔
439
        S: Serializer,
1✔
440
    {
1✔
441
        serializer.serialize_str(&self.to_string())
1✔
442
    }
1✔
443
}
444

445
/// Helper struct for deserializing a `SpatialReferencce`
446
struct SpatialReferenceDeserializeVisitor;
447

448
impl<'de> Visitor<'de> for SpatialReferenceDeserializeVisitor {
449
    type Value = SpatialReference;
450

451
    fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
×
452
        formatter.write_str("a spatial reference in the form authority:code")
×
453
    }
×
454

455
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
16✔
456
    where
16✔
457
        E: serde::de::Error,
16✔
458
    {
16✔
459
        v.parse().map_err(serde::de::Error::custom)
16✔
460
    }
16✔
461
}
462

463
impl<'de> Deserialize<'de> for SpatialReference {
464
    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
16✔
465
    where
16✔
466
        D: Deserializer<'de>,
16✔
467
    {
16✔
468
        deserializer.deserialize_str(SpatialReferenceDeserializeVisitor)
16✔
469
    }
16✔
470
}
471

472
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
×
473
pub enum SpatialReferenceOption {
474
    SpatialReference(SpatialReference),
475
    Unreferenced,
476
}
477

478
impl From<geoengine_datatypes::spatial_reference::SpatialReferenceOption>
479
    for SpatialReferenceOption
480
{
481
    fn from(value: geoengine_datatypes::spatial_reference::SpatialReferenceOption) -> Self {
17✔
482
        match value {
17✔
483
            geoengine_datatypes::spatial_reference::SpatialReferenceOption::SpatialReference(s) => {
17✔
484
                Self::SpatialReference(s.into())
17✔
485
            }
486
            geoengine_datatypes::spatial_reference::SpatialReferenceOption::Unreferenced => {
487
                Self::Unreferenced
×
488
            }
489
        }
490
    }
17✔
491
}
492

493
impl From<SpatialReferenceOption>
494
    for geoengine_datatypes::spatial_reference::SpatialReferenceOption
495
{
496
    fn from(value: SpatialReferenceOption) -> Self {
8✔
497
        match value {
8✔
498
            SpatialReferenceOption::SpatialReference(sr) => Self::SpatialReference(sr.into()),
8✔
499
            SpatialReferenceOption::Unreferenced => Self::Unreferenced,
×
500
        }
501
    }
8✔
502
}
503

504
impl From<SpatialReference> for SpatialReferenceOption {
505
    fn from(spatial_reference: SpatialReference) -> Self {
10✔
506
        Self::SpatialReference(spatial_reference)
10✔
507
    }
10✔
508
}
509

510
impl std::fmt::Display for SpatialReferenceOption {
511
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
6✔
512
        match self {
6✔
513
            SpatialReferenceOption::SpatialReference(p) => write!(f, "{p}"),
6✔
514
            SpatialReferenceOption::Unreferenced => Ok(()),
×
515
        }
516
    }
6✔
517
}
518

519
impl Serialize for SpatialReferenceOption {
520
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
6✔
521
    where
6✔
522
        S: Serializer,
6✔
523
    {
6✔
524
        serializer.serialize_str(&self.to_string())
6✔
525
    }
6✔
526
}
527

528
/// Helper struct for deserializing a `SpatialReferenceOption`
529
struct SpatialReferenceOptionDeserializeVisitor;
530

531
impl<'de> Visitor<'de> for SpatialReferenceOptionDeserializeVisitor {
532
    type Value = SpatialReferenceOption;
533

534
    fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
×
535
        formatter.write_str("a spatial reference in the form authority:code")
×
536
    }
×
537

538
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
10✔
539
    where
10✔
540
        E: serde::de::Error,
10✔
541
    {
10✔
542
        if v.is_empty() {
10✔
543
            return Ok(SpatialReferenceOption::Unreferenced);
×
544
        }
10✔
545

546
        let spatial_reference: SpatialReference = v.parse().map_err(serde::de::Error::custom)?;
10✔
547

548
        Ok(spatial_reference.into())
10✔
549
    }
10✔
550
}
551

552
impl<'de> Deserialize<'de> for SpatialReferenceOption {
553
    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
10✔
554
    where
10✔
555
        D: Deserializer<'de>,
10✔
556
    {
10✔
557
        deserializer.deserialize_str(SpatialReferenceOptionDeserializeVisitor)
10✔
558
    }
10✔
559
}
560

561
impl From<Option<SpatialReference>> for SpatialReferenceOption {
562
    fn from(option: Option<SpatialReference>) -> Self {
×
563
        match option {
×
564
            Some(p) => SpatialReferenceOption::SpatialReference(p),
×
565
            None => SpatialReferenceOption::Unreferenced,
×
566
        }
567
    }
×
568
}
569

570
impl From<SpatialReferenceOption> for Option<SpatialReference> {
571
    fn from(option: SpatialReferenceOption) -> Self {
12✔
572
        match option {
12✔
573
            SpatialReferenceOption::SpatialReference(p) => Some(p),
12✔
574
            SpatialReferenceOption::Unreferenced => None,
×
575
        }
576
    }
12✔
577
}
578

579
impl ToSql for SpatialReferenceOption {
580
    fn to_sql(
×
581
        &self,
×
582
        ty: &postgres_types::Type,
×
583
        out: &mut bytes::BytesMut,
×
584
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>>
×
585
    where
×
586
        Self: Sized,
×
587
    {
×
588
        match self {
×
589
            SpatialReferenceOption::SpatialReference(sref) => sref.to_sql(ty, out),
×
590
            SpatialReferenceOption::Unreferenced => Ok(postgres_types::IsNull::Yes),
×
591
        }
592
    }
×
593

594
    fn accepts(ty: &postgres_types::Type) -> bool
×
595
    where
×
596
        Self: Sized,
×
597
    {
×
598
        <SpatialReference as ToSql>::accepts(ty)
×
599
    }
×
600

601
    fn to_sql_checked(
×
602
        &self,
×
603
        ty: &postgres_types::Type,
×
604
        out: &mut bytes::BytesMut,
×
605
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
×
606
        match self {
×
607
            SpatialReferenceOption::SpatialReference(sref) => sref.to_sql_checked(ty, out),
×
608
            SpatialReferenceOption::Unreferenced => Ok(postgres_types::IsNull::Yes),
×
609
        }
610
    }
×
611
}
612

613
impl<'a> FromSql<'a> for SpatialReferenceOption {
614
    fn from_sql(
×
615
        ty: &postgres_types::Type,
×
616
        raw: &'a [u8],
×
617
    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
×
618
        Ok(SpatialReferenceOption::SpatialReference(
×
619
            SpatialReference::from_sql(ty, raw)?,
×
620
        ))
621
    }
×
622

623
    fn from_sql_null(
×
624
        _: &postgres_types::Type,
×
625
    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
×
626
        Ok(SpatialReferenceOption::Unreferenced)
×
627
    }
×
628

629
    fn accepts(ty: &postgres_types::Type) -> bool {
×
630
        <SpatialReference as FromSql>::accepts(ty)
×
631
    }
×
632
}
633

634
/// An enum that contains all possible vector data types
635
#[derive(
636
    Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Deserialize, Serialize, Copy, Clone, ToSchema,
24✔
637
)]
638
pub enum VectorDataType {
639
    Data,
640
    MultiPoint,
641
    MultiLineString,
642
    MultiPolygon,
643
}
644

645
impl From<geoengine_datatypes::collections::VectorDataType> for VectorDataType {
646
    fn from(value: geoengine_datatypes::collections::VectorDataType) -> Self {
2✔
647
        match value {
2✔
648
            geoengine_datatypes::collections::VectorDataType::Data => Self::Data,
×
649
            geoengine_datatypes::collections::VectorDataType::MultiPoint => Self::MultiPoint,
2✔
650
            geoengine_datatypes::collections::VectorDataType::MultiLineString => {
651
                Self::MultiLineString
×
652
            }
653
            geoengine_datatypes::collections::VectorDataType::MultiPolygon => Self::MultiPolygon,
×
654
        }
655
    }
2✔
656
}
657

658
impl From<VectorDataType> for geoengine_datatypes::collections::VectorDataType {
659
    fn from(value: VectorDataType) -> Self {
8✔
660
        match value {
8✔
661
            VectorDataType::Data => Self::Data,
×
662
            VectorDataType::MultiPoint => Self::MultiPoint,
8✔
663
            VectorDataType::MultiLineString => Self::MultiLineString,
×
664
            VectorDataType::MultiPolygon => Self::MultiPolygon,
×
665
        }
666
    }
8✔
667
}
668

669
#[derive(
670
    Clone,
×
671
    Copy,
672
    Debug,
×
673
    Deserialize,
70✔
674
    PartialEq,
8✔
675
    PartialOrd,
×
676
    Serialize,
17✔
677
    Default,
×
678
    ToSchema,
2✔
679
    ToSql,
×
680
    FromSql,
×
681
)]
682
pub struct Coordinate2D {
683
    pub x: f64,
684
    pub y: f64,
685
}
686

687
impl From<geoengine_datatypes::primitives::Coordinate2D> for Coordinate2D {
688
    fn from(coordinate: geoengine_datatypes::primitives::Coordinate2D) -> Self {
64✔
689
        Self {
64✔
690
            x: coordinate.x,
64✔
691
            y: coordinate.y,
64✔
692
        }
64✔
693
    }
64✔
694
}
695

696
impl From<Coordinate2D> for geoengine_datatypes::primitives::Coordinate2D {
697
    fn from(coordinate: Coordinate2D) -> Self {
14✔
698
        Self {
14✔
699
            x: coordinate.x,
14✔
700
            y: coordinate.y,
14✔
701
        }
14✔
702
    }
14✔
703
}
704

705
#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Debug, ToSchema, ToSql, FromSql)]
5✔
706
#[serde(rename_all = "camelCase")]
707
/// A bounding box that includes all border points.
708
/// Note: may degenerate to a point!
709
pub struct BoundingBox2D {
710
    pub lower_left_coordinate: Coordinate2D,
711
    pub upper_right_coordinate: Coordinate2D,
712
}
713

714
impl From<geoengine_datatypes::primitives::BoundingBox2D> for BoundingBox2D {
715
    fn from(bbox: geoengine_datatypes::primitives::BoundingBox2D) -> Self {
26✔
716
        Self {
26✔
717
            lower_left_coordinate:
26✔
718
                geoengine_datatypes::primitives::AxisAlignedRectangle::lower_left(&bbox).into(),
26✔
719
            upper_right_coordinate:
26✔
720
                geoengine_datatypes::primitives::AxisAlignedRectangle::upper_right(&bbox).into(),
26✔
721
        }
26✔
722
    }
26✔
723
}
724

725
impl From<BoundingBox2D> for geoengine_datatypes::primitives::BoundingBox2D {
726
    fn from(bbox: BoundingBox2D) -> Self {
1✔
727
        Self::new_unchecked(
1✔
728
            bbox.lower_left_coordinate.into(),
1✔
729
            bbox.upper_right_coordinate.into(),
1✔
730
        )
1✔
731
    }
1✔
732
}
733

734
/// An object that composes the date and a timestamp with time zone.
735
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ToSchema)]
2✔
736
pub struct DateTime {
737
    datetime: chrono::DateTime<chrono::Utc>,
738
}
739

740
impl FromStr for DateTime {
741
    type Err = geoengine_datatypes::primitives::DateTimeError;
742

743
    fn from_str(input: &str) -> Result<Self, Self::Err> {
×
744
        let date_time = chrono::DateTime::<chrono::FixedOffset>::from_str(input).map_err(|e| {
×
745
            Self::Err::DateParse {
×
746
                source: Box::new(e),
×
747
            }
×
748
        })?;
×
749

750
        Ok(date_time.into())
×
751
    }
×
752
}
753

754
impl From<chrono::DateTime<chrono::FixedOffset>> for DateTime {
755
    fn from(datetime: chrono::DateTime<chrono::FixedOffset>) -> Self {
×
756
        Self {
×
757
            datetime: datetime.into(),
×
758
        }
×
759
    }
×
760
}
761

762
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
42✔
763
#[serde(rename_all = "camelCase")]
764
pub enum FeatureDataType {
765
    Category,
766
    Int,
767
    Float,
768
    Text,
769
    Bool,
770
    DateTime,
771
}
772

773
impl From<geoengine_datatypes::primitives::FeatureDataType> for FeatureDataType {
774
    fn from(value: geoengine_datatypes::primitives::FeatureDataType) -> Self {
2✔
775
        match value {
2✔
776
            geoengine_datatypes::primitives::FeatureDataType::Category => Self::Category,
×
777
            geoengine_datatypes::primitives::FeatureDataType::Int => Self::Int,
1✔
778
            geoengine_datatypes::primitives::FeatureDataType::Float => Self::Float,
×
779
            geoengine_datatypes::primitives::FeatureDataType::Text => Self::Text,
1✔
780
            geoengine_datatypes::primitives::FeatureDataType::Bool => Self::Bool,
×
781
            geoengine_datatypes::primitives::FeatureDataType::DateTime => Self::DateTime,
×
782
        }
783
    }
2✔
784
}
785

786
impl From<FeatureDataType> for geoengine_datatypes::primitives::FeatureDataType {
787
    fn from(value: FeatureDataType) -> Self {
20✔
788
        match value {
20✔
789
            FeatureDataType::Category => Self::Category,
×
790
            FeatureDataType::Int => Self::Int,
4✔
791
            FeatureDataType::Float => Self::Float,
4✔
792
            FeatureDataType::Text => Self::Text,
12✔
793
            FeatureDataType::Bool => Self::Bool,
×
794
            FeatureDataType::DateTime => Self::DateTime,
×
795
        }
796
    }
20✔
797
}
798

799
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
50✔
800
#[serde(rename_all = "camelCase", tag = "type")]
801
pub enum Measurement {
802
    Unitless,
803
    Continuous(ContinuousMeasurement),
804
    Classification(ClassificationMeasurement),
805
}
806

807
impl From<geoengine_datatypes::primitives::Measurement> for Measurement {
808
    fn from(value: geoengine_datatypes::primitives::Measurement) -> Self {
6✔
809
        match value {
6✔
810
            geoengine_datatypes::primitives::Measurement::Unitless => Self::Unitless,
6✔
811
            geoengine_datatypes::primitives::Measurement::Continuous(cm) => {
×
812
                Self::Continuous(cm.into())
×
813
            }
814
            geoengine_datatypes::primitives::Measurement::Classification(cm) => {
×
815
                Self::Classification(cm.into())
×
816
            }
817
        }
818
    }
6✔
819
}
820

821
impl From<Measurement> for geoengine_datatypes::primitives::Measurement {
822
    fn from(value: Measurement) -> Self {
24✔
823
        match value {
24✔
824
            Measurement::Unitless => Self::Unitless,
24✔
825
            Measurement::Continuous(cm) => Self::Continuous(cm.into()),
×
826
            Measurement::Classification(cm) => Self::Classification(cm.into()),
×
827
        }
828
    }
24✔
829
}
830

831
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
2✔
832
pub struct ContinuousMeasurement {
833
    pub measurement: String,
834
    pub unit: Option<String>,
835
}
836

837
impl From<geoengine_datatypes::primitives::ContinuousMeasurement> for ContinuousMeasurement {
838
    fn from(value: geoengine_datatypes::primitives::ContinuousMeasurement) -> Self {
×
839
        Self {
×
840
            measurement: value.measurement,
×
841
            unit: value.unit,
×
842
        }
×
843
    }
×
844
}
845

846
impl From<ContinuousMeasurement> for geoengine_datatypes::primitives::ContinuousMeasurement {
847
    fn from(value: ContinuousMeasurement) -> Self {
×
848
        Self {
×
849
            measurement: value.measurement,
×
850
            unit: value.unit,
×
851
        }
×
852
    }
×
853
}
854

855
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
2✔
856
#[serde(
857
    try_from = "SerializableClassificationMeasurement",
858
    into = "SerializableClassificationMeasurement"
859
)]
860
pub struct ClassificationMeasurement {
861
    pub measurement: String,
862
    pub classes: HashMap<u8, String>,
863
}
864

865
impl From<geoengine_datatypes::primitives::ClassificationMeasurement>
866
    for ClassificationMeasurement
867
{
868
    fn from(value: geoengine_datatypes::primitives::ClassificationMeasurement) -> Self {
×
869
        Self {
×
870
            measurement: value.measurement,
×
871
            classes: value.classes,
×
872
        }
×
873
    }
×
874
}
875

876
impl From<ClassificationMeasurement>
877
    for geoengine_datatypes::primitives::ClassificationMeasurement
878
{
879
    fn from(value: ClassificationMeasurement) -> Self {
×
880
        Self {
×
881
            measurement: value.measurement,
×
882
            classes: value.classes,
×
883
        }
×
884
    }
×
885
}
886

887
/// A type that is solely for serde's serializability.
888
/// You cannot serialize floats as JSON map keys.
889
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
×
890
pub struct SerializableClassificationMeasurement {
891
    pub measurement: String,
892
    // use a BTreeMap to preserve the order of the keys
893
    pub classes: BTreeMap<String, String>,
894
}
895

896
impl From<ClassificationMeasurement> for SerializableClassificationMeasurement {
897
    fn from(measurement: ClassificationMeasurement) -> Self {
×
898
        let mut classes = BTreeMap::new();
×
899
        for (k, v) in measurement.classes {
×
900
            classes.insert(k.to_string(), v);
×
901
        }
×
902
        Self {
×
903
            measurement: measurement.measurement,
×
904
            classes,
×
905
        }
×
906
    }
×
907
}
908

909
impl TryFrom<SerializableClassificationMeasurement> for ClassificationMeasurement {
910
    type Error = <u8 as FromStr>::Err;
911

912
    fn try_from(measurement: SerializableClassificationMeasurement) -> Result<Self, Self::Error> {
×
913
        let mut classes = HashMap::with_capacity(measurement.classes.len());
×
914
        for (k, v) in measurement.classes {
×
915
            classes.insert(k.parse::<u8>()?, v);
×
916
        }
917
        Ok(Self {
×
918
            measurement: measurement.measurement,
×
919
            classes,
×
920
        })
×
921
    }
×
922
}
923

924
/// A partition of space that include the upper left but excludes the lower right coordinate
925
#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Debug, ToSchema, FromSql, ToSql)]
20✔
926
#[serde(rename_all = "camelCase")]
927
pub struct SpatialPartition2D {
928
    pub upper_left_coordinate: Coordinate2D,
929
    pub lower_right_coordinate: Coordinate2D,
930
}
931

932
impl From<geoengine_datatypes::primitives::SpatialPartition2D> for SpatialPartition2D {
933
    fn from(value: geoengine_datatypes::primitives::SpatialPartition2D) -> Self {
4✔
934
        Self {
4✔
935
            upper_left_coordinate: value.upper_left().into(),
4✔
936
            lower_right_coordinate: value.lower_right().into(),
4✔
937
        }
4✔
938
    }
4✔
939
}
940

941
impl From<SpatialPartition2D> for geoengine_datatypes::primitives::SpatialPartition2D {
942
    fn from(value: SpatialPartition2D) -> Self {
4✔
943
        Self::new_unchecked(
4✔
944
            value.upper_left_coordinate.into(),
4✔
945
            value.lower_right_coordinate.into(),
4✔
946
        )
4✔
947
    }
4✔
948
}
949

950
/// A spatio-temporal rectangle with a specified resolution
951
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
6✔
952
#[serde(rename_all = "camelCase")]
953
#[aliases(
954
    VectorQueryRectangle = QueryRectangle<BoundingBox2D>,
955
    RasterQueryRectangle = QueryRectangle<SpatialPartition2D>,
956
    PlotQueryRectangle = QueryRectangle<BoundingBox2D>)
957
]
958
pub struct QueryRectangle<SpatialBounds> {
959
    pub spatial_bounds: SpatialBounds,
960
    pub time_interval: TimeInterval,
961
    pub spatial_resolution: SpatialResolution,
962
}
963

964
/// The spatial resolution in SRS units
965
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize, ToSchema)]
20✔
966
pub struct SpatialResolution {
967
    pub x: f64,
968
    pub y: f64,
969
}
970

971
impl From<geoengine_datatypes::primitives::SpatialResolution> for SpatialResolution {
972
    fn from(value: geoengine_datatypes::primitives::SpatialResolution) -> Self {
4✔
973
        Self {
4✔
974
            x: value.x,
4✔
975
            y: value.y,
4✔
976
        }
4✔
977
    }
4✔
978
}
979

980
impl From<SpatialResolution> for geoengine_datatypes::primitives::SpatialResolution {
981
    fn from(value: SpatialResolution) -> Self {
4✔
982
        Self {
4✔
983
            x: value.x,
4✔
984
            y: value.y,
4✔
985
        }
4✔
986
    }
4✔
987
}
988

989
#[derive(
990
    Clone, Copy, Serialize, PartialEq, Eq, PartialOrd, Ord, Debug, ToSchema, FromSql, ToSql,
32✔
991
)]
992
#[repr(C)]
993
#[postgres(transparent)]
994
pub struct TimeInstance(i64);
995

996
impl FromStr for TimeInstance {
997
    type Err = geoengine_datatypes::primitives::DateTimeError;
998

999
    fn from_str(s: &str) -> Result<Self, Self::Err> {
×
1000
        let date_time = DateTime::from_str(s)?;
×
1001
        Ok(date_time.into())
×
1002
    }
×
1003
}
1004

1005
impl From<geoengine_datatypes::primitives::TimeInstance> for TimeInstance {
1006
    fn from(value: geoengine_datatypes::primitives::TimeInstance) -> Self {
136✔
1007
        Self(value.inner())
136✔
1008
    }
136✔
1009
}
1010

1011
impl From<TimeInstance> for geoengine_datatypes::primitives::TimeInstance {
1012
    fn from(value: TimeInstance) -> Self {
68✔
1013
        geoengine_datatypes::primitives::TimeInstance::from_millis_unchecked(value.inner())
68✔
1014
    }
68✔
1015
}
1016

1017
impl From<DateTime> for TimeInstance {
1018
    fn from(datetime: DateTime) -> Self {
×
1019
        Self::from(&datetime)
×
1020
    }
×
1021
}
1022

1023
impl From<&DateTime> for TimeInstance {
1024
    fn from(datetime: &DateTime) -> Self {
×
1025
        geoengine_datatypes::primitives::TimeInstance::from_millis_unchecked(
×
1026
            datetime.datetime.timestamp_millis(),
×
1027
        )
×
1028
        .into()
×
1029
    }
×
1030
}
1031

1032
impl TimeInstance {
1033
    pub const fn inner(self) -> i64 {
68✔
1034
        self.0
68✔
1035
    }
68✔
1036
}
1037

1038
impl<'de> Deserialize<'de> for TimeInstance {
1039
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
16✔
1040
    where
16✔
1041
        D: serde::Deserializer<'de>,
16✔
1042
    {
16✔
1043
        struct IsoStringOrUnixTimestamp;
16✔
1044

16✔
1045
        impl<'de> serde::de::Visitor<'de> for IsoStringOrUnixTimestamp {
16✔
1046
            type Value = TimeInstance;
16✔
1047

16✔
1048
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
16✔
1049
                formatter.write_str("RFC 3339 timestamp string or Unix timestamp integer")
×
1050
            }
×
1051

16✔
1052
            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
16✔
1053
            where
×
1054
                E: serde::de::Error,
×
1055
            {
×
1056
                TimeInstance::from_str(value).map_err(E::custom)
×
1057
            }
×
1058

16✔
1059
            fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
16✔
1060
            where
16✔
1061
                E: serde::de::Error,
16✔
1062
            {
16✔
1063
                geoengine_datatypes::primitives::TimeInstance::from_millis(v)
16✔
1064
                    .map(Into::into)
16✔
1065
                    .map_err(E::custom)
16✔
1066
            }
16✔
1067

16✔
1068
            fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
16✔
1069
            where
16✔
1070
                E: serde::de::Error,
16✔
1071
            {
16✔
1072
                Self::visit_i64(self, v as i64)
16✔
1073
            }
16✔
1074
        }
16✔
1075

16✔
1076
        deserializer.deserialize_any(IsoStringOrUnixTimestamp)
16✔
1077
    }
16✔
1078
}
1079

1080
/// A time granularity.
1081
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema, FromSql, ToSql)]
8✔
1082
#[serde(rename_all = "camelCase")]
1083
pub enum TimeGranularity {
1084
    Millis,
1085
    Seconds,
1086
    Minutes,
1087
    Hours,
1088
    Days,
1089
    Months,
1090
    Years,
1091
}
1092

1093
impl From<geoengine_datatypes::primitives::TimeGranularity> for TimeGranularity {
1094
    fn from(value: geoengine_datatypes::primitives::TimeGranularity) -> Self {
4✔
1095
        match value {
4✔
1096
            geoengine_datatypes::primitives::TimeGranularity::Millis => Self::Millis,
×
1097
            geoengine_datatypes::primitives::TimeGranularity::Seconds => Self::Seconds,
×
1098
            geoengine_datatypes::primitives::TimeGranularity::Minutes => Self::Minutes,
×
1099
            geoengine_datatypes::primitives::TimeGranularity::Hours => Self::Hours,
×
1100
            geoengine_datatypes::primitives::TimeGranularity::Days => Self::Days,
×
1101
            geoengine_datatypes::primitives::TimeGranularity::Months => Self::Months,
4✔
1102
            geoengine_datatypes::primitives::TimeGranularity::Years => Self::Years,
×
1103
        }
1104
    }
4✔
1105
}
1106

1107
impl From<TimeGranularity> for geoengine_datatypes::primitives::TimeGranularity {
1108
    fn from(value: TimeGranularity) -> Self {
4✔
1109
        match value {
4✔
1110
            TimeGranularity::Millis => Self::Millis,
×
1111
            TimeGranularity::Seconds => Self::Seconds,
×
1112
            TimeGranularity::Minutes => Self::Minutes,
×
1113
            TimeGranularity::Hours => Self::Hours,
×
1114
            TimeGranularity::Days => Self::Days,
×
1115
            TimeGranularity::Months => Self::Months,
4✔
1116
            TimeGranularity::Years => Self::Years,
×
1117
        }
1118
    }
4✔
1119
}
1120

1121
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema, FromSql, ToSql)]
20✔
1122
pub struct TimeStep {
1123
    pub granularity: TimeGranularity,
1124
    pub step: u32, // TODO: ensure on deserialization it is > 0
1125
}
1126

1127
impl From<geoengine_datatypes::primitives::TimeStep> for TimeStep {
1128
    fn from(value: geoengine_datatypes::primitives::TimeStep) -> Self {
4✔
1129
        Self {
4✔
1130
            granularity: value.granularity.into(),
4✔
1131
            step: value.step,
4✔
1132
        }
4✔
1133
    }
4✔
1134
}
1135

1136
impl From<TimeStep> for geoengine_datatypes::primitives::TimeStep {
1137
    fn from(value: TimeStep) -> Self {
4✔
1138
        Self {
4✔
1139
            granularity: value.granularity.into(),
4✔
1140
            step: value.step,
4✔
1141
        }
4✔
1142
    }
4✔
1143
}
1144

1145
/// Stores time intervals in ms in close-open semantic [start, end)
1146
#[derive(Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ToSql, FromSql, ToSchema)]
40✔
1147
pub struct TimeInterval {
1148
    start: TimeInstance,
1149
    end: TimeInstance,
1150
}
1151

1152
impl From<TimeInterval> for geoengine_datatypes::primitives::TimeInterval {
1153
    fn from(value: TimeInterval) -> Self {
34✔
1154
        geoengine_datatypes::primitives::TimeInterval::new_unchecked::<
34✔
1155
            geoengine_datatypes::primitives::TimeInstance,
34✔
1156
            geoengine_datatypes::primitives::TimeInstance,
34✔
1157
        >(value.start.into(), value.end.into())
34✔
1158
    }
34✔
1159
}
1160

1161
impl From<geoengine_datatypes::primitives::TimeInterval> for TimeInterval {
1162
    fn from(value: geoengine_datatypes::primitives::TimeInterval) -> Self {
60✔
1163
        Self {
60✔
1164
            start: value.start().into(),
60✔
1165
            end: value.end().into(),
60✔
1166
        }
60✔
1167
    }
60✔
1168
}
1169

1170
impl core::fmt::Debug for TimeInterval {
1171
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
×
1172
        write!(
×
1173
            f,
×
1174
            "TimeInterval [{}, {})",
×
1175
            self.start.inner(),
×
1176
            &self.end.inner()
×
1177
        )
×
1178
    }
×
1179
}
1180

1181
#[derive(
1182
    Debug,
×
1183
    Ord,
×
1184
    PartialOrd,
×
1185
    Eq,
1186
    PartialEq,
×
1187
    Hash,
×
1188
    Deserialize,
8✔
1189
    Serialize,
5✔
1190
    Copy,
1191
    Clone,
×
1192
    ToSchema,
2✔
1193
    FromSql,
×
1194
    ToSql,
×
1195
)]
1196
pub enum RasterDataType {
1197
    U8,
1198
    U16,
1199
    U32,
1200
    U64,
1201
    I8,
1202
    I16,
1203
    I32,
1204
    I64,
1205
    F32,
1206
    F64,
1207
}
1208

1209
impl From<geoengine_datatypes::raster::RasterDataType> for RasterDataType {
1210
    fn from(value: geoengine_datatypes::raster::RasterDataType) -> Self {
4✔
1211
        match value {
4✔
1212
            geoengine_datatypes::raster::RasterDataType::U8 => Self::U8,
4✔
1213
            geoengine_datatypes::raster::RasterDataType::U16 => Self::U16,
×
1214
            geoengine_datatypes::raster::RasterDataType::U32 => Self::U32,
×
1215
            geoengine_datatypes::raster::RasterDataType::U64 => Self::U64,
×
1216
            geoengine_datatypes::raster::RasterDataType::I8 => Self::I8,
×
1217
            geoengine_datatypes::raster::RasterDataType::I16 => Self::I16,
×
1218
            geoengine_datatypes::raster::RasterDataType::I32 => Self::I32,
×
1219
            geoengine_datatypes::raster::RasterDataType::I64 => Self::I64,
×
1220
            geoengine_datatypes::raster::RasterDataType::F32 => Self::F32,
×
1221
            geoengine_datatypes::raster::RasterDataType::F64 => Self::F64,
×
1222
        }
1223
    }
4✔
1224
}
1225

1226
impl From<RasterDataType> for geoengine_datatypes::raster::RasterDataType {
1227
    fn from(value: RasterDataType) -> Self {
4✔
1228
        match value {
4✔
1229
            RasterDataType::U8 => Self::U8,
4✔
1230
            RasterDataType::U16 => Self::U16,
×
1231
            RasterDataType::U32 => Self::U32,
×
1232
            RasterDataType::U64 => Self::U64,
×
1233
            RasterDataType::I8 => Self::I8,
×
1234
            RasterDataType::I16 => Self::I16,
×
1235
            RasterDataType::I32 => Self::I32,
×
1236
            RasterDataType::I64 => Self::I64,
×
1237
            RasterDataType::F32 => Self::F32,
×
1238
            RasterDataType::F64 => Self::F64,
×
1239
        }
1240
    }
4✔
1241
}
1242

1243
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
×
1244
#[serde(rename_all = "UPPERCASE")]
1245
pub enum ResamplingMethod {
1246
    Nearest,
1247
    Average,
1248
    Bilinear,
1249
    Cubic,
1250
    CubicSpline,
1251
    Lanczos,
1252
}
1253

1254
impl std::fmt::Display for ResamplingMethod {
1255
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
1256
        match self {
×
1257
            ResamplingMethod::Nearest => write!(f, "NEAREST"),
×
1258
            ResamplingMethod::Average => write!(f, "AVERAGE"),
×
1259
            ResamplingMethod::Bilinear => write!(f, "BILINEAR"),
×
1260
            ResamplingMethod::Cubic => write!(f, "CUBIC"),
×
1261
            ResamplingMethod::CubicSpline => write!(f, "CUBICSPLINE"),
×
1262
            ResamplingMethod::Lanczos => write!(f, "LANCZOS"),
×
1263
        }
1264
    }
×
1265
}
1266

1267
impl From<ResamplingMethod> for geoengine_datatypes::util::gdal::ResamplingMethod {
1268
    fn from(value: ResamplingMethod) -> Self {
×
1269
        match value {
×
1270
            ResamplingMethod::Nearest => Self::Nearest,
×
1271
            ResamplingMethod::Average => Self::Average,
×
1272
            ResamplingMethod::Bilinear => Self::Bilinear,
×
1273
            ResamplingMethod::Cubic => Self::Cubic,
×
1274
            ResamplingMethod::CubicSpline => Self::CubicSpline,
×
1275
            ResamplingMethod::Lanczos => Self::Lanczos,
×
1276
        }
1277
    }
×
1278
}
1279

1280
/// `RgbaColor` defines a 32 bit RGB color with alpha value
1281
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
×
1282
pub struct RgbaColor(pub [u8; 4]);
1283

1284
// manual implementation utoipa generates an integer field
1285
impl<'a> ToSchema<'a> for RgbaColor {
1286
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
1287
        use utoipa::openapi::*;
2✔
1288
        (
2✔
1289
            "RgbaColor",
2✔
1290
            ArrayBuilder::new()
2✔
1291
                .items(ObjectBuilder::new().schema_type(SchemaType::Integer))
2✔
1292
                .min_items(Some(4))
2✔
1293
                .max_items(Some(4))
2✔
1294
                .into(),
2✔
1295
        )
2✔
1296
    }
2✔
1297
}
1298

1299
impl From<geoengine_datatypes::operations::image::RgbaColor> for RgbaColor {
1300
    fn from(color: geoengine_datatypes::operations::image::RgbaColor) -> Self {
×
1301
        Self(color.into_inner())
×
1302
    }
×
1303
}
1304

1305
impl From<RgbaColor> for geoengine_datatypes::operations::image::RgbaColor {
1306
    fn from(color: RgbaColor) -> Self {
×
1307
        Self::new(color.0[0], color.0[1], color.0[2], color.0[3])
×
1308
    }
×
1309
}
1310

1311
/// A container type for breakpoints that specify a value to color mapping
1312
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
×
1313
pub struct Breakpoint {
1314
    pub value: NotNanF64,
1315
    pub color: RgbaColor,
1316
}
1317

1318
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
×
1319
pub struct NotNanF64(NotNan<f64>);
1320

1321
impl From<NotNan<f64>> for NotNanF64 {
1322
    fn from(value: NotNan<f64>) -> Self {
×
1323
        Self(value)
×
1324
    }
×
1325
}
1326

1327
impl From<NotNanF64> for NotNan<f64> {
1328
    fn from(value: NotNanF64) -> Self {
×
1329
        value.0
×
1330
    }
×
1331
}
1332

1333
impl ToSql for NotNanF64 {
1334
    fn to_sql(
×
1335
        &self,
×
1336
        ty: &postgres_types::Type,
×
1337
        w: &mut bytes::BytesMut,
×
1338
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
×
1339
        <f64 as ToSql>::to_sql(&self.0.into_inner(), ty, w)
×
1340
    }
×
1341

1342
    fn accepts(ty: &postgres_types::Type) -> bool {
×
1343
        <f64 as ToSql>::accepts(ty)
×
1344
    }
×
1345

1346
    postgres_types::to_sql_checked!();
1347
}
1348

1349
impl<'a> FromSql<'a> for NotNanF64 {
1350
    fn from_sql(
×
1351
        ty: &postgres_types::Type,
×
1352
        raw: &'a [u8],
×
1353
    ) -> Result<NotNanF64, Box<dyn std::error::Error + Sync + Send>> {
×
1354
        let value = <f64 as FromSql>::from_sql(ty, raw)?;
×
1355

1356
        Ok(NotNanF64(value.try_into()?))
×
1357
    }
×
1358

1359
    fn accepts(ty: &postgres_types::Type) -> bool {
×
1360
        <f64 as FromSql>::accepts(ty)
×
1361
    }
×
1362
}
1363

1364
// manual implementation because of NotNan
1365
impl<'a> ToSchema<'a> for Breakpoint {
1366
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
1367
        use utoipa::openapi::*;
2✔
1368
        (
2✔
1369
            "Breakpoint",
2✔
1370
            ObjectBuilder::new()
2✔
1371
                .property("value", Object::with_type(SchemaType::Number))
2✔
1372
                .property("color", Ref::from_schema_name("RgbaColor"))
2✔
1373
                .into(),
2✔
1374
        )
2✔
1375
    }
2✔
1376
}
1377

1378
impl From<geoengine_datatypes::operations::image::Breakpoint> for Breakpoint {
1379
    fn from(breakpoint: geoengine_datatypes::operations::image::Breakpoint) -> Self {
×
1380
        Self {
×
1381
            value: breakpoint.value.into(),
×
1382
            color: breakpoint.color.into(),
×
1383
        }
×
1384
    }
×
1385
}
1386

1387
#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
2✔
1388
#[serde(untagged, rename_all = "camelCase", into = "OverUnderColors")]
1389
pub enum DefaultColors {
1390
    #[serde(rename_all = "camelCase")]
1391
    DefaultColor { default_color: RgbaColor },
1392
    #[serde(rename_all = "camelCase")]
1393
    OverUnder(OverUnderColors),
1394
}
1395

1396
#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
2✔
1397
#[serde(rename_all = "camelCase")]
1398
pub struct OverUnderColors {
1399
    pub over_color: RgbaColor,
1400
    pub under_color: RgbaColor,
1401
}
1402

1403
impl From<DefaultColors> for OverUnderColors {
1404
    fn from(value: DefaultColors) -> Self {
×
1405
        match value {
×
1406
            DefaultColors::DefaultColor { default_color } => Self {
×
1407
                over_color: default_color,
×
1408
                under_color: default_color,
×
1409
            },
×
1410
            DefaultColors::OverUnder(over_under) => over_under,
×
1411
        }
1412
    }
×
1413
}
1414

1415
impl From<DefaultColors> for geoengine_datatypes::operations::image::DefaultColors {
1416
    fn from(value: DefaultColors) -> Self {
×
1417
        match value {
×
1418
            DefaultColors::DefaultColor { default_color } => Self::DefaultColor {
×
1419
                default_color: default_color.into(),
×
1420
            },
×
1421
            DefaultColors::OverUnder(OverUnderColors {
1422
                over_color,
×
1423
                under_color,
×
1424
            }) => Self::OverUnder {
×
1425
                over_color: over_color.into(),
×
1426
                under_color: under_color.into(),
×
1427
            },
×
1428
        }
1429
    }
×
1430
}
1431

1432
impl From<geoengine_datatypes::operations::image::DefaultColors> for DefaultColors {
1433
    fn from(value: geoengine_datatypes::operations::image::DefaultColors) -> Self {
×
1434
        match value {
×
1435
            geoengine_datatypes::operations::image::DefaultColors::DefaultColor {
1436
                default_color,
×
1437
            } => Self::DefaultColor {
×
1438
                default_color: default_color.into(),
×
1439
            },
×
1440
            geoengine_datatypes::operations::image::DefaultColors::OverUnder {
1441
                over_color,
×
1442
                under_color,
×
1443
            } => Self::OverUnder(OverUnderColors {
×
1444
                over_color: over_color.into(),
×
1445
                under_color: under_color.into(),
×
1446
            }),
×
1447
        }
1448
    }
×
1449
}
1450

1451
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
2✔
1452
#[serde(rename_all = "camelCase")]
1453
pub struct LinearGradient {
1454
    pub breakpoints: Vec<Breakpoint>,
1455
    pub no_data_color: RgbaColor,
1456
    #[serde(flatten)]
1457
    pub color_fields: DefaultColors,
1458
}
1459

1460
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
2✔
1461
#[serde(rename_all = "camelCase")]
1462
pub struct LogarithmicGradient {
1463
    pub breakpoints: Vec<Breakpoint>,
1464
    pub no_data_color: RgbaColor,
1465
    #[serde(flatten)]
1466
    pub color_fields: DefaultColors,
1467
}
1468

1469
/// A colorizer specifies a mapping between raster values and an output image
1470
/// There are different variants that perform different kinds of mapping.
1471
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
2✔
1472
#[serde(rename_all = "camelCase", tag = "type")]
1473
pub enum Colorizer {
1474
    #[serde(rename_all = "camelCase")]
1475
    LinearGradient(LinearGradient),
1476
    #[serde(rename_all = "camelCase")]
1477
    LogarithmicGradient(LogarithmicGradient),
1478
    #[serde(rename_all = "camelCase")]
1479
    Palette {
1480
        colors: Palette,
1481
        no_data_color: RgbaColor,
1482
        default_color: RgbaColor,
1483
    },
1484
    Rgba,
1485
}
1486

1487
impl From<geoengine_datatypes::operations::image::Colorizer> for Colorizer {
1488
    fn from(v: geoengine_datatypes::operations::image::Colorizer) -> Self {
×
1489
        match v {
×
1490
            geoengine_datatypes::operations::image::Colorizer::LinearGradient {
1491
                breakpoints,
×
1492
                no_data_color,
×
1493
                default_colors: color_fields,
×
1494
            } => Self::LinearGradient(LinearGradient {
×
1495
                breakpoints: breakpoints
×
1496
                    .into_iter()
×
1497
                    .map(Into::into)
×
1498
                    .collect::<Vec<Breakpoint>>(),
×
1499
                no_data_color: no_data_color.into(),
×
1500
                color_fields: color_fields.into(),
×
1501
            }),
×
1502
            geoengine_datatypes::operations::image::Colorizer::LogarithmicGradient {
1503
                breakpoints,
×
1504
                no_data_color,
×
1505
                default_colors: color_fields,
×
1506
            } => Self::LogarithmicGradient(LogarithmicGradient {
×
1507
                breakpoints: breakpoints
×
1508
                    .into_iter()
×
1509
                    .map(Into::into)
×
1510
                    .collect::<Vec<Breakpoint>>(),
×
1511
                no_data_color: no_data_color.into(),
×
1512
                color_fields: color_fields.into(),
×
1513
            }),
×
1514
            geoengine_datatypes::operations::image::Colorizer::Palette {
1515
                colors,
×
1516
                no_data_color,
×
1517
                default_color,
×
1518
            } => Self::Palette {
×
1519
                colors: colors.into(),
×
1520
                no_data_color: no_data_color.into(),
×
1521
                default_color: default_color.into(),
×
1522
            },
×
1523
            geoengine_datatypes::operations::image::Colorizer::Rgba => Self::Rgba,
×
1524
        }
1525
    }
×
1526
}
1527

1528
/// A map from value to color
1529
///
1530
/// It is assumed that is has at least one and at most 256 entries.
1531
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
2✔
1532
#[serde(try_from = "SerializablePalette", into = "SerializablePalette")]
1533
pub struct Palette(pub HashMap<NotNan<f64>, RgbaColor>);
1534

1535
impl From<geoengine_datatypes::operations::image::Palette> for Palette {
1536
    fn from(palette: geoengine_datatypes::operations::image::Palette) -> Self {
×
1537
        Self(
×
1538
            palette
×
1539
                .into_inner()
×
1540
                .into_iter()
×
1541
                .map(|(value, color)| (value, color.into()))
×
1542
                .collect(),
×
1543
        )
×
1544
    }
×
1545
}
1546

1547
/// A type that is solely for serde's serializability.
1548
/// You cannot serialize floats as JSON map keys.
1549
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
×
1550
pub struct SerializablePalette(HashMap<String, RgbaColor>);
1551

1552
impl From<Palette> for SerializablePalette {
1553
    fn from(palette: Palette) -> Self {
×
1554
        Self(
×
1555
            palette
×
1556
                .0
×
1557
                .into_iter()
×
1558
                .map(|(k, v)| (k.to_string(), v))
×
1559
                .collect(),
×
1560
        )
×
1561
    }
×
1562
}
1563

1564
impl TryFrom<SerializablePalette> for Palette {
1565
    type Error = <NotNan<f64> as FromStr>::Err;
1566

1567
    fn try_from(palette: SerializablePalette) -> Result<Self, Self::Error> {
×
1568
        let mut inner = HashMap::<NotNan<f64>, RgbaColor>::with_capacity(palette.0.len());
×
1569
        for (k, v) in palette.0 {
×
1570
            inner.insert(k.parse()?, v);
×
1571
        }
1572
        Ok(Self(inner))
×
1573
    }
×
1574
}
1575

1576
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Hash, Eq, PartialOrd, Ord, ToSchema)]
2✔
1577
pub struct RasterPropertiesKey {
1578
    pub domain: Option<String>,
1579
    pub key: String,
1580
}
1581

1582
impl From<geoengine_datatypes::raster::RasterPropertiesKey> for RasterPropertiesKey {
1583
    fn from(value: geoengine_datatypes::raster::RasterPropertiesKey) -> Self {
×
1584
        Self {
×
1585
            domain: value.domain,
×
1586
            key: value.key,
×
1587
        }
×
1588
    }
×
1589
}
1590

1591
impl From<RasterPropertiesKey> for geoengine_datatypes::raster::RasterPropertiesKey {
1592
    fn from(value: RasterPropertiesKey) -> Self {
×
1593
        Self {
×
1594
            domain: value.domain,
×
1595
            key: value.key,
×
1596
        }
×
1597
    }
×
1598
}
1599

1600
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
2✔
1601
pub enum RasterPropertiesEntryType {
1602
    Number,
1603
    String,
1604
}
1605

1606
impl From<geoengine_datatypes::raster::RasterPropertiesEntryType> for RasterPropertiesEntryType {
1607
    fn from(value: geoengine_datatypes::raster::RasterPropertiesEntryType) -> Self {
×
1608
        match value {
×
1609
            geoengine_datatypes::raster::RasterPropertiesEntryType::Number => Self::Number,
×
1610
            geoengine_datatypes::raster::RasterPropertiesEntryType::String => Self::String,
×
1611
        }
1612
    }
×
1613
}
1614

1615
impl From<RasterPropertiesEntryType> for geoengine_datatypes::raster::RasterPropertiesEntryType {
1616
    fn from(value: RasterPropertiesEntryType) -> Self {
×
1617
        match value {
×
1618
            RasterPropertiesEntryType::Number => Self::Number,
×
1619
            RasterPropertiesEntryType::String => Self::String,
×
1620
        }
1621
    }
×
1622
}
1623

1624
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, ToSchema)]
28✔
1625
pub struct DateTimeParseFormat {
1626
    fmt: String,
1627
    has_tz: bool,
1628
    has_time: bool,
1629
}
1630

1631
impl From<geoengine_datatypes::primitives::DateTimeParseFormat> for DateTimeParseFormat {
1632
    fn from(value: geoengine_datatypes::primitives::DateTimeParseFormat) -> Self {
4✔
1633
        Self {
4✔
1634
            fmt: value._to_parse_format().to_string(),
4✔
1635
            has_tz: value.has_tz(),
4✔
1636
            has_time: value.has_time(),
4✔
1637
        }
4✔
1638
    }
4✔
1639
}
1640

1641
impl From<DateTimeParseFormat> for geoengine_datatypes::primitives::DateTimeParseFormat {
1642
    fn from(value: DateTimeParseFormat) -> Self {
4✔
1643
        Self::custom(value.fmt)
4✔
1644
    }
4✔
1645
}
1646

1647
impl DateTimeParseFormat {
1648
    // this is used as default value
1649
    pub fn unix() -> Self {
×
1650
        geoengine_datatypes::primitives::DateTimeParseFormat::unix().into()
×
1651
    }
×
1652
}
1653

1654
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
2✔
1655
pub struct NoGeometry;
1656

1657
impl From<geoengine_datatypes::primitives::NoGeometry> for NoGeometry {
1658
    fn from(_: geoengine_datatypes::primitives::NoGeometry) -> Self {
×
1659
        Self {}
×
1660
    }
×
1661
}
1662

1663
impl From<NoGeometry> for geoengine_datatypes::primitives::NoGeometry {
1664
    fn from(_: NoGeometry) -> Self {
×
1665
        Self {}
×
1666
    }
×
1667
}
1668

1669
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1670
pub struct MultiPoint {
1671
    pub coordinates: Vec<Coordinate2D>,
1672
}
1673

1674
impl From<geoengine_datatypes::primitives::MultiPoint> for MultiPoint {
1675
    fn from(value: geoengine_datatypes::primitives::MultiPoint) -> Self {
×
1676
        Self {
×
1677
            coordinates: value.points().iter().map(|x| (*x).into()).collect(),
×
1678
        }
×
1679
    }
×
1680
}
1681

1682
impl From<MultiPoint> for geoengine_datatypes::primitives::MultiPoint {
1683
    fn from(value: MultiPoint) -> Self {
×
1684
        Self::new(value.coordinates.into_iter().map(Into::into).collect()).unwrap()
×
1685
    }
×
1686
}
1687

1688
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1689
pub struct MultiLineString {
1690
    pub coordinates: Vec<Vec<Coordinate2D>>,
1691
}
1692

1693
impl From<geoengine_datatypes::primitives::MultiLineString> for MultiLineString {
1694
    fn from(value: geoengine_datatypes::primitives::MultiLineString) -> Self {
×
1695
        Self {
×
1696
            coordinates: value
×
1697
                .lines()
×
1698
                .iter()
×
1699
                .map(|x| x.iter().map(|x| (*x).into()).collect())
×
1700
                .collect(),
×
1701
        }
×
1702
    }
×
1703
}
1704

1705
impl From<MultiLineString> for geoengine_datatypes::primitives::MultiLineString {
1706
    fn from(value: MultiLineString) -> Self {
×
1707
        Self::new(
×
1708
            value
×
1709
                .coordinates
×
1710
                .into_iter()
×
1711
                .map(|x| x.into_iter().map(Into::into).collect())
×
1712
                .collect(),
×
1713
        )
×
1714
        .unwrap()
×
1715
    }
×
1716
}
1717

1718
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1719
pub struct MultiPolygon {
1720
    pub polygons: Vec<Vec<Vec<Coordinate2D>>>,
1721
}
1722

1723
impl From<geoengine_datatypes::primitives::MultiPolygon> for MultiPolygon {
1724
    fn from(value: geoengine_datatypes::primitives::MultiPolygon) -> Self {
×
1725
        Self {
×
1726
            polygons: value
×
1727
                .polygons()
×
1728
                .iter()
×
1729
                .map(|x| {
×
1730
                    x.iter()
×
1731
                        .map(|y| y.iter().map(|y| (*y).into()).collect())
×
1732
                        .collect()
×
1733
                })
×
1734
                .collect(),
×
1735
        }
×
1736
    }
×
1737
}
1738

1739
impl From<MultiPolygon> for geoengine_datatypes::primitives::MultiPolygon {
1740
    fn from(value: MultiPolygon) -> Self {
×
1741
        Self::new(
×
1742
            value
×
1743
                .polygons
×
1744
                .iter()
×
1745
                .map(|x| {
×
1746
                    x.iter()
×
1747
                        .map(|y| y.iter().map(|y| (*y).into()).collect())
×
1748
                        .collect()
×
1749
                })
×
1750
                .collect(),
×
1751
        )
×
1752
        .unwrap()
×
1753
    }
×
1754
}
1755

1756
#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)]
4✔
1757
pub struct StringPair((String, String));
1758

1759
pub type GdalConfigOption = StringPair;
1760
pub type AxisLabels = StringPair;
1761

1762
impl<'a> ToSchema<'a> for StringPair {
1763
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
6✔
1764
        use utoipa::openapi::*;
6✔
1765
        (
6✔
1766
            "StringPair",
6✔
1767
            ArrayBuilder::new()
6✔
1768
                .items(Object::with_type(SchemaType::String))
6✔
1769
                .min_items(Some(2))
6✔
1770
                .max_items(Some(2))
6✔
1771
                .into(),
6✔
1772
        )
6✔
1773
    }
6✔
1774

1775
    fn aliases() -> Vec<(&'a str, utoipa::openapi::Schema)> {
6✔
1776
        let utoipa::openapi::RefOr::T(unpacked_schema) = Self::schema().1 else {
6✔
1777
            unreachable!()
×
1778
        };
1779
        vec![
6✔
1780
            ("GdalConfigOption", unpacked_schema.clone()),
6✔
1781
            ("AxisLabels", unpacked_schema),
6✔
1782
        ]
6✔
1783
    }
6✔
1784
}
1785

1786
impl From<(String, String)> for StringPair {
1787
    fn from(value: (String, String)) -> Self {
26✔
1788
        Self(value)
26✔
1789
    }
26✔
1790
}
1791

1792
impl From<StringPair> for (String, String) {
1793
    fn from(value: StringPair) -> Self {
×
1794
        value.0
×
1795
    }
×
1796
}
1797

1798
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize, ToSchema)]
2✔
1799
pub enum PlotOutputFormat {
1800
    JsonPlain,
1801
    JsonVega,
1802
    ImagePng,
1803
}
1804

1805
#[derive(Default, Debug, Clone, Copy, PartialEq, Serialize, PartialOrd, Deserialize, ToSchema)]
6✔
1806
pub struct CacheTtlSeconds(u32);
1807

1808
const MAX_CACHE_TTL_SECONDS: u32 = 31_536_000; // 1 year
1809

1810
impl CacheTtlSeconds {
1811
    pub fn new(seconds: u32) -> Self {
×
1812
        Self(seconds.min(MAX_CACHE_TTL_SECONDS))
×
1813
    }
×
1814

1815
    pub fn max() -> Self {
×
1816
        Self(MAX_CACHE_TTL_SECONDS)
×
1817
    }
×
1818

1819
    pub fn seconds(self) -> u32 {
×
1820
        self.0
×
1821
    }
×
1822
}
1823

1824
impl From<geoengine_datatypes::primitives::CacheTtlSeconds> for CacheTtlSeconds {
1825
    fn from(value: geoengine_datatypes::primitives::CacheTtlSeconds) -> Self {
5✔
1826
        Self(value.seconds())
5✔
1827
    }
5✔
1828
}
1829

1830
impl From<CacheTtlSeconds> for geoengine_datatypes::primitives::CacheTtlSeconds {
1831
    fn from(value: CacheTtlSeconds) -> Self {
8✔
1832
        Self::new(value.0)
8✔
1833
    }
8✔
1834
}
1835

1836
impl ToSql for CacheTtlSeconds {
1837
    fn to_sql(
×
1838
        &self,
×
1839
        ty: &postgres_types::Type,
×
1840
        w: &mut bytes::BytesMut,
×
1841
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
×
1842
        <i32 as ToSql>::to_sql(&(self.0 as i32), ty, w)
×
1843
    }
×
1844

1845
    fn accepts(ty: &postgres_types::Type) -> bool {
×
1846
        <i32 as ToSql>::accepts(ty)
×
1847
    }
×
1848

1849
    postgres_types::to_sql_checked!();
1850
}
1851

1852
impl<'a> FromSql<'a> for CacheTtlSeconds {
1853
    fn from_sql(
×
1854
        ty: &postgres_types::Type,
×
1855
        raw: &'a [u8],
×
1856
    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
×
1857
        Ok(Self(<i32 as FromSql>::from_sql(ty, raw)? as u32))
×
1858
    }
×
1859

1860
    fn accepts(ty: &postgres_types::Type) -> bool {
×
1861
        <i32 as FromSql>::accepts(ty)
×
1862
    }
×
1863
}
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