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

geo-engine / geoengine / 6251704882

20 Sep 2023 04:53PM UTC coverage: 89.512% (-0.4%) from 89.876%
6251704882

push

github

web-flow
Merge pull request #879 from geo-engine/api-type-separation

Api-type-separation

2926 of 2926 new or added lines in 60 files covered. (100.0%)

109181 of 121974 relevant lines covered (89.51%)

59537.64 hits per line

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

42.81
/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<'a> ToSchema<'a> for SpatialReference {
381
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
382
        use utoipa::openapi::*;
2✔
383
        (
2✔
384
            "SpatialReference",
2✔
385
            ObjectBuilder::new().schema_type(SchemaType::String).into(),
2✔
386
        )
2✔
387
    }
2✔
388
}
389

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

399
impl From<SpatialReference> for geoengine_datatypes::spatial_reference::SpatialReference {
400
    fn from(value: SpatialReference) -> Self {
12✔
401
        geoengine_datatypes::spatial_reference::SpatialReference::new(
12✔
402
            value.authority.into(),
12✔
403
            value.code,
12✔
404
        )
12✔
405
    }
12✔
406
}
407

408
impl SpatialReference {
409
    pub fn new(authority: SpatialReferenceAuthority, code: u32) -> Self {
39✔
410
        Self { authority, code }
39✔
411
    }
39✔
412

413
    pub fn authority(&self) -> &SpatialReferenceAuthority {
1✔
414
        &self.authority
1✔
415
    }
1✔
416

417
    pub fn code(self) -> u32 {
1✔
418
        self.code
1✔
419
    }
1✔
420
}
421

422
impl FromStr for SpatialReference {
423
    type Err = error::Error;
424

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

34✔
428
        match (split.next(), split.next(), split.next()) {
34✔
429
            (Some(authority), Some(code), None) => Ok(Self::new(
34✔
430
                authority.parse()?,
34✔
431
                code.parse::<u32>().context(error::ParseU32)?,
34✔
432
            )),
433
            _ => Err(error::Error::InvalidSpatialReferenceString {
×
434
                spatial_reference_string: s.into(),
×
435
            }),
×
436
        }
437
    }
34✔
438
}
439

440
impl std::fmt::Display for SpatialReference {
441
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
7✔
442
        write!(f, "{}:{}", self.authority, self.code)
7✔
443
    }
7✔
444
}
445

446
impl Serialize for SpatialReference {
447
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
1✔
448
    where
1✔
449
        S: Serializer,
1✔
450
    {
1✔
451
        serializer.serialize_str(&self.to_string())
1✔
452
    }
1✔
453
}
454

455
/// Helper struct for deserializing a `SpatialReferencce`
456
struct SpatialReferenceDeserializeVisitor;
457

458
impl<'de> Visitor<'de> for SpatialReferenceDeserializeVisitor {
459
    type Value = SpatialReference;
460

461
    fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
×
462
        formatter.write_str("a spatial reference in the form authority:code")
×
463
    }
×
464

465
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
16✔
466
    where
16✔
467
        E: serde::de::Error,
16✔
468
    {
16✔
469
        v.parse().map_err(serde::de::Error::custom)
16✔
470
    }
16✔
471
}
472

473
impl<'de> Deserialize<'de> for SpatialReference {
474
    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
16✔
475
    where
16✔
476
        D: Deserializer<'de>,
16✔
477
    {
16✔
478
        deserializer.deserialize_str(SpatialReferenceDeserializeVisitor)
16✔
479
    }
16✔
480
}
481

482
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, ToSchema)]
2✔
483
pub enum SpatialReferenceOption {
484
    SpatialReference(SpatialReference),
485
    Unreferenced,
486
}
487

488
impl From<geoengine_datatypes::spatial_reference::SpatialReferenceOption>
489
    for SpatialReferenceOption
490
{
491
    fn from(value: geoengine_datatypes::spatial_reference::SpatialReferenceOption) -> Self {
17✔
492
        match value {
17✔
493
            geoengine_datatypes::spatial_reference::SpatialReferenceOption::SpatialReference(s) => {
17✔
494
                Self::SpatialReference(s.into())
17✔
495
            }
496
            geoengine_datatypes::spatial_reference::SpatialReferenceOption::Unreferenced => {
497
                Self::Unreferenced
×
498
            }
499
        }
500
    }
17✔
501
}
502

503
impl From<SpatialReferenceOption>
504
    for geoengine_datatypes::spatial_reference::SpatialReferenceOption
505
{
506
    fn from(value: SpatialReferenceOption) -> Self {
8✔
507
        match value {
8✔
508
            SpatialReferenceOption::SpatialReference(sr) => Self::SpatialReference(sr.into()),
8✔
509
            SpatialReferenceOption::Unreferenced => Self::Unreferenced,
×
510
        }
511
    }
8✔
512
}
513

514
impl From<SpatialReference> for SpatialReferenceOption {
515
    fn from(spatial_reference: SpatialReference) -> Self {
10✔
516
        Self::SpatialReference(spatial_reference)
10✔
517
    }
10✔
518
}
519

520
impl std::fmt::Display for SpatialReferenceOption {
521
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
6✔
522
        match self {
6✔
523
            SpatialReferenceOption::SpatialReference(p) => write!(f, "{p}"),
6✔
524
            SpatialReferenceOption::Unreferenced => Ok(()),
×
525
        }
526
    }
6✔
527
}
528

529
impl Serialize for SpatialReferenceOption {
530
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
6✔
531
    where
6✔
532
        S: Serializer,
6✔
533
    {
6✔
534
        serializer.serialize_str(&self.to_string())
6✔
535
    }
6✔
536
}
537

538
/// Helper struct for deserializing a `SpatialReferenceOption`
539
struct SpatialReferenceOptionDeserializeVisitor;
540

541
impl<'de> Visitor<'de> for SpatialReferenceOptionDeserializeVisitor {
542
    type Value = SpatialReferenceOption;
543

544
    fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
×
545
        formatter.write_str("a spatial reference in the form authority:code")
×
546
    }
×
547

548
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
10✔
549
    where
10✔
550
        E: serde::de::Error,
10✔
551
    {
10✔
552
        if v.is_empty() {
10✔
553
            return Ok(SpatialReferenceOption::Unreferenced);
×
554
        }
10✔
555

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

558
        Ok(spatial_reference.into())
10✔
559
    }
10✔
560
}
561

562
impl<'de> Deserialize<'de> for SpatialReferenceOption {
563
    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
10✔
564
    where
10✔
565
        D: Deserializer<'de>,
10✔
566
    {
10✔
567
        deserializer.deserialize_str(SpatialReferenceOptionDeserializeVisitor)
10✔
568
    }
10✔
569
}
570

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

580
impl From<SpatialReferenceOption> for Option<SpatialReference> {
581
    fn from(option: SpatialReferenceOption) -> Self {
12✔
582
        match option {
12✔
583
            SpatialReferenceOption::SpatialReference(p) => Some(p),
12✔
584
            SpatialReferenceOption::Unreferenced => None,
×
585
        }
586
    }
12✔
587
}
588

589
impl ToSql for SpatialReferenceOption {
590
    fn to_sql(
×
591
        &self,
×
592
        ty: &postgres_types::Type,
×
593
        out: &mut bytes::BytesMut,
×
594
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>>
×
595
    where
×
596
        Self: Sized,
×
597
    {
×
598
        match self {
×
599
            SpatialReferenceOption::SpatialReference(sref) => sref.to_sql(ty, out),
×
600
            SpatialReferenceOption::Unreferenced => Ok(postgres_types::IsNull::Yes),
×
601
        }
602
    }
×
603

604
    fn accepts(ty: &postgres_types::Type) -> bool
×
605
    where
×
606
        Self: Sized,
×
607
    {
×
608
        <SpatialReference as ToSql>::accepts(ty)
×
609
    }
×
610

611
    fn to_sql_checked(
×
612
        &self,
×
613
        ty: &postgres_types::Type,
×
614
        out: &mut bytes::BytesMut,
×
615
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
×
616
        match self {
×
617
            SpatialReferenceOption::SpatialReference(sref) => sref.to_sql_checked(ty, out),
×
618
            SpatialReferenceOption::Unreferenced => Ok(postgres_types::IsNull::Yes),
×
619
        }
620
    }
×
621
}
622

623
impl<'a> FromSql<'a> for SpatialReferenceOption {
624
    fn from_sql(
×
625
        ty: &postgres_types::Type,
×
626
        raw: &'a [u8],
×
627
    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
×
628
        Ok(SpatialReferenceOption::SpatialReference(
×
629
            SpatialReference::from_sql(ty, raw)?,
×
630
        ))
631
    }
×
632

633
    fn from_sql_null(
×
634
        _: &postgres_types::Type,
×
635
    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
×
636
        Ok(SpatialReferenceOption::Unreferenced)
×
637
    }
×
638

639
    fn accepts(ty: &postgres_types::Type) -> bool {
×
640
        <SpatialReference as FromSql>::accepts(ty)
×
641
    }
×
642
}
643

644
/// An enum that contains all possible vector data types
645
#[derive(
646
    Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Deserialize, Serialize, Copy, Clone, ToSchema,
24✔
647
)]
648
pub enum VectorDataType {
649
    Data,
650
    MultiPoint,
651
    MultiLineString,
652
    MultiPolygon,
653
}
654

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

668
impl From<VectorDataType> for geoengine_datatypes::collections::VectorDataType {
669
    fn from(value: VectorDataType) -> Self {
8✔
670
        match value {
8✔
671
            VectorDataType::Data => Self::Data,
×
672
            VectorDataType::MultiPoint => Self::MultiPoint,
8✔
673
            VectorDataType::MultiLineString => Self::MultiLineString,
×
674
            VectorDataType::MultiPolygon => Self::MultiPolygon,
×
675
        }
676
    }
8✔
677
}
678

679
#[derive(
680
    Clone,
×
681
    Copy,
682
    Debug,
×
683
    Deserialize,
70✔
684
    PartialEq,
8✔
685
    PartialOrd,
×
686
    Serialize,
17✔
687
    Default,
×
688
    ToSchema,
2✔
689
    ToSql,
×
690
    FromSql,
×
691
)]
692
pub struct Coordinate2D {
693
    pub x: f64,
694
    pub y: f64,
695
}
696

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

706
impl From<Coordinate2D> for geoengine_datatypes::primitives::Coordinate2D {
707
    fn from(coordinate: Coordinate2D) -> Self {
14✔
708
        Self {
14✔
709
            x: coordinate.x,
14✔
710
            y: coordinate.y,
14✔
711
        }
14✔
712
    }
14✔
713
}
714

715
#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Debug, ToSchema, ToSql, FromSql)]
5✔
716
#[serde(rename_all = "camelCase")]
717
/// A bounding box that includes all border points.
718
/// Note: may degenerate to a point!
719
pub struct BoundingBox2D {
720
    pub lower_left_coordinate: Coordinate2D,
721
    pub upper_right_coordinate: Coordinate2D,
722
}
723

724
impl From<geoengine_datatypes::primitives::BoundingBox2D> for BoundingBox2D {
725
    fn from(bbox: geoengine_datatypes::primitives::BoundingBox2D) -> Self {
26✔
726
        Self {
26✔
727
            lower_left_coordinate:
26✔
728
                geoengine_datatypes::primitives::AxisAlignedRectangle::lower_left(&bbox).into(),
26✔
729
            upper_right_coordinate:
26✔
730
                geoengine_datatypes::primitives::AxisAlignedRectangle::upper_right(&bbox).into(),
26✔
731
        }
26✔
732
    }
26✔
733
}
734

735
impl From<BoundingBox2D> for geoengine_datatypes::primitives::BoundingBox2D {
736
    fn from(bbox: BoundingBox2D) -> Self {
1✔
737
        Self::new_unchecked(
1✔
738
            bbox.lower_left_coordinate.into(),
1✔
739
            bbox.upper_right_coordinate.into(),
1✔
740
        )
1✔
741
    }
1✔
742
}
743

744
/// An object that composes the date and a timestamp with time zone.
745
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ToSchema)]
2✔
746
pub struct DateTime {
747
    datetime: chrono::DateTime<chrono::Utc>,
748
}
749

750
impl FromStr for DateTime {
751
    type Err = geoengine_datatypes::primitives::DateTimeError;
752

753
    fn from_str(input: &str) -> Result<Self, Self::Err> {
×
754
        let date_time = chrono::DateTime::<chrono::FixedOffset>::from_str(input).map_err(|e| {
×
755
            Self::Err::DateParse {
×
756
                source: Box::new(e),
×
757
            }
×
758
        })?;
×
759

760
        Ok(date_time.into())
×
761
    }
×
762
}
763

764
impl From<chrono::DateTime<chrono::FixedOffset>> for DateTime {
765
    fn from(datetime: chrono::DateTime<chrono::FixedOffset>) -> Self {
×
766
        Self {
×
767
            datetime: datetime.into(),
×
768
        }
×
769
    }
×
770
}
771

772
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
42✔
773
#[serde(rename_all = "camelCase")]
774
pub enum FeatureDataType {
775
    Category,
776
    Int,
777
    Float,
778
    Text,
779
    Bool,
780
    DateTime,
781
}
782

783
impl From<geoengine_datatypes::primitives::FeatureDataType> for FeatureDataType {
784
    fn from(value: geoengine_datatypes::primitives::FeatureDataType) -> Self {
2✔
785
        match value {
2✔
786
            geoengine_datatypes::primitives::FeatureDataType::Category => Self::Category,
×
787
            geoengine_datatypes::primitives::FeatureDataType::Int => Self::Int,
1✔
788
            geoengine_datatypes::primitives::FeatureDataType::Float => Self::Float,
×
789
            geoengine_datatypes::primitives::FeatureDataType::Text => Self::Text,
1✔
790
            geoengine_datatypes::primitives::FeatureDataType::Bool => Self::Bool,
×
791
            geoengine_datatypes::primitives::FeatureDataType::DateTime => Self::DateTime,
×
792
        }
793
    }
2✔
794
}
795

796
impl From<FeatureDataType> for geoengine_datatypes::primitives::FeatureDataType {
797
    fn from(value: FeatureDataType) -> Self {
20✔
798
        match value {
20✔
799
            FeatureDataType::Category => Self::Category,
×
800
            FeatureDataType::Int => Self::Int,
4✔
801
            FeatureDataType::Float => Self::Float,
4✔
802
            FeatureDataType::Text => Self::Text,
12✔
803
            FeatureDataType::Bool => Self::Bool,
×
804
            FeatureDataType::DateTime => Self::DateTime,
×
805
        }
806
    }
20✔
807
}
808

809
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
50✔
810
#[serde(rename_all = "camelCase", tag = "type")]
811
pub enum Measurement {
812
    Unitless,
813
    Continuous(ContinuousMeasurement),
814
    Classification(ClassificationMeasurement),
815
}
816

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

831
impl From<Measurement> for geoengine_datatypes::primitives::Measurement {
832
    fn from(value: Measurement) -> Self {
24✔
833
        match value {
24✔
834
            Measurement::Unitless => Self::Unitless,
24✔
835
            Measurement::Continuous(cm) => Self::Continuous(cm.into()),
×
836
            Measurement::Classification(cm) => Self::Classification(cm.into()),
×
837
        }
838
    }
24✔
839
}
840

841
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
2✔
842
pub struct ContinuousMeasurement {
843
    pub measurement: String,
844
    pub unit: Option<String>,
845
}
846

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

856
impl From<ContinuousMeasurement> for geoengine_datatypes::primitives::ContinuousMeasurement {
857
    fn from(value: ContinuousMeasurement) -> Self {
×
858
        Self {
×
859
            measurement: value.measurement,
×
860
            unit: value.unit,
×
861
        }
×
862
    }
×
863
}
864

865
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
2✔
866
#[serde(
867
    try_from = "SerializableClassificationMeasurement",
868
    into = "SerializableClassificationMeasurement"
869
)]
870
pub struct ClassificationMeasurement {
871
    pub measurement: String,
872
    pub classes: HashMap<u8, String>,
873
}
874

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

886
impl From<ClassificationMeasurement>
887
    for geoengine_datatypes::primitives::ClassificationMeasurement
888
{
889
    fn from(value: ClassificationMeasurement) -> Self {
×
890
        Self {
×
891
            measurement: value.measurement,
×
892
            classes: value.classes,
×
893
        }
×
894
    }
×
895
}
896

897
/// A type that is solely for serde's serializability.
898
/// You cannot serialize floats as JSON map keys.
899
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
×
900
pub struct SerializableClassificationMeasurement {
901
    pub measurement: String,
902
    // use a BTreeMap to preserve the order of the keys
903
    pub classes: BTreeMap<String, String>,
904
}
905

906
impl From<ClassificationMeasurement> for SerializableClassificationMeasurement {
907
    fn from(measurement: ClassificationMeasurement) -> Self {
×
908
        let mut classes = BTreeMap::new();
×
909
        for (k, v) in measurement.classes {
×
910
            classes.insert(k.to_string(), v);
×
911
        }
×
912
        Self {
×
913
            measurement: measurement.measurement,
×
914
            classes,
×
915
        }
×
916
    }
×
917
}
918

919
impl TryFrom<SerializableClassificationMeasurement> for ClassificationMeasurement {
920
    type Error = <u8 as FromStr>::Err;
921

922
    fn try_from(measurement: SerializableClassificationMeasurement) -> Result<Self, Self::Error> {
×
923
        let mut classes = HashMap::with_capacity(measurement.classes.len());
×
924
        for (k, v) in measurement.classes {
×
925
            classes.insert(k.parse::<u8>()?, v);
×
926
        }
927
        Ok(Self {
×
928
            measurement: measurement.measurement,
×
929
            classes,
×
930
        })
×
931
    }
×
932
}
933

934
/// A partition of space that include the upper left but excludes the lower right coordinate
935
#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Debug, ToSchema, FromSql, ToSql)]
20✔
936
#[serde(rename_all = "camelCase")]
937
pub struct SpatialPartition2D {
938
    pub upper_left_coordinate: Coordinate2D,
939
    pub lower_right_coordinate: Coordinate2D,
940
}
941

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

951
impl From<SpatialPartition2D> for geoengine_datatypes::primitives::SpatialPartition2D {
952
    fn from(value: SpatialPartition2D) -> Self {
4✔
953
        Self::new_unchecked(
4✔
954
            value.upper_left_coordinate.into(),
4✔
955
            value.lower_right_coordinate.into(),
4✔
956
        )
4✔
957
    }
4✔
958
}
959

960
/// A spatio-temporal rectangle with a specified resolution
961
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
6✔
962
#[serde(rename_all = "camelCase")]
963
#[aliases(
964
    VectorQueryRectangle = QueryRectangle<BoundingBox2D>,
965
    RasterQueryRectangle = QueryRectangle<SpatialPartition2D>,
966
    PlotQueryRectangle = QueryRectangle<BoundingBox2D>)
967
]
968
pub struct QueryRectangle<SpatialBounds> {
969
    pub spatial_bounds: SpatialBounds,
970
    pub time_interval: TimeInterval,
971
    pub spatial_resolution: SpatialResolution,
972
}
973

974
/// The spatial resolution in SRS units
975
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize, ToSchema)]
20✔
976
pub struct SpatialResolution {
977
    pub x: f64,
978
    pub y: f64,
979
}
980

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

990
impl From<SpatialResolution> for geoengine_datatypes::primitives::SpatialResolution {
991
    fn from(value: SpatialResolution) -> Self {
4✔
992
        Self {
4✔
993
            x: value.x,
4✔
994
            y: value.y,
4✔
995
        }
4✔
996
    }
4✔
997
}
998

999
#[derive(
1000
    Clone, Copy, Serialize, PartialEq, Eq, PartialOrd, Ord, Debug, ToSchema, FromSql, ToSql,
32✔
1001
)]
1002
#[repr(C)]
1003
#[postgres(transparent)]
1004
pub struct TimeInstance(i64);
1005

1006
impl FromStr for TimeInstance {
1007
    type Err = geoengine_datatypes::primitives::DateTimeError;
1008

1009
    fn from_str(s: &str) -> Result<Self, Self::Err> {
×
1010
        let date_time = DateTime::from_str(s)?;
×
1011
        Ok(date_time.into())
×
1012
    }
×
1013
}
1014

1015
impl From<geoengine_datatypes::primitives::TimeInstance> for TimeInstance {
1016
    fn from(value: geoengine_datatypes::primitives::TimeInstance) -> Self {
136✔
1017
        Self(value.inner())
136✔
1018
    }
136✔
1019
}
1020

1021
impl From<TimeInstance> for geoengine_datatypes::primitives::TimeInstance {
1022
    fn from(value: TimeInstance) -> Self {
68✔
1023
        geoengine_datatypes::primitives::TimeInstance::from_millis_unchecked(value.inner())
68✔
1024
    }
68✔
1025
}
1026

1027
impl From<DateTime> for TimeInstance {
1028
    fn from(datetime: DateTime) -> Self {
×
1029
        Self::from(&datetime)
×
1030
    }
×
1031
}
1032

1033
impl From<&DateTime> for TimeInstance {
1034
    fn from(datetime: &DateTime) -> Self {
×
1035
        geoengine_datatypes::primitives::TimeInstance::from_millis_unchecked(
×
1036
            datetime.datetime.timestamp_millis(),
×
1037
        )
×
1038
        .into()
×
1039
    }
×
1040
}
1041

1042
impl TimeInstance {
1043
    pub const fn inner(self) -> i64 {
68✔
1044
        self.0
68✔
1045
    }
68✔
1046
}
1047

1048
impl<'de> Deserialize<'de> for TimeInstance {
1049
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
16✔
1050
    where
16✔
1051
        D: serde::Deserializer<'de>,
16✔
1052
    {
16✔
1053
        struct IsoStringOrUnixTimestamp;
16✔
1054

16✔
1055
        impl<'de> serde::de::Visitor<'de> for IsoStringOrUnixTimestamp {
16✔
1056
            type Value = TimeInstance;
16✔
1057

16✔
1058
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
16✔
1059
                formatter.write_str("RFC 3339 timestamp string or Unix timestamp integer")
×
1060
            }
×
1061

16✔
1062
            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
16✔
1063
            where
×
1064
                E: serde::de::Error,
×
1065
            {
×
1066
                TimeInstance::from_str(value).map_err(E::custom)
×
1067
            }
×
1068

16✔
1069
            fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
16✔
1070
            where
16✔
1071
                E: serde::de::Error,
16✔
1072
            {
16✔
1073
                geoengine_datatypes::primitives::TimeInstance::from_millis(v)
16✔
1074
                    .map(Into::into)
16✔
1075
                    .map_err(E::custom)
16✔
1076
            }
16✔
1077

16✔
1078
            fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
16✔
1079
            where
16✔
1080
                E: serde::de::Error,
16✔
1081
            {
16✔
1082
                Self::visit_i64(self, v as i64)
16✔
1083
            }
16✔
1084
        }
16✔
1085

16✔
1086
        deserializer.deserialize_any(IsoStringOrUnixTimestamp)
16✔
1087
    }
16✔
1088
}
1089

1090
/// A time granularity.
1091
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema, FromSql, ToSql)]
8✔
1092
#[serde(rename_all = "camelCase")]
1093
pub enum TimeGranularity {
1094
    Millis,
1095
    Seconds,
1096
    Minutes,
1097
    Hours,
1098
    Days,
1099
    Months,
1100
    Years,
1101
}
1102

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

1117
impl From<TimeGranularity> for geoengine_datatypes::primitives::TimeGranularity {
1118
    fn from(value: TimeGranularity) -> Self {
4✔
1119
        match value {
4✔
1120
            TimeGranularity::Millis => Self::Millis,
×
1121
            TimeGranularity::Seconds => Self::Seconds,
×
1122
            TimeGranularity::Minutes => Self::Minutes,
×
1123
            TimeGranularity::Hours => Self::Hours,
×
1124
            TimeGranularity::Days => Self::Days,
×
1125
            TimeGranularity::Months => Self::Months,
4✔
1126
            TimeGranularity::Years => Self::Years,
×
1127
        }
1128
    }
4✔
1129
}
1130

1131
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema, FromSql, ToSql)]
20✔
1132
pub struct TimeStep {
1133
    pub granularity: TimeGranularity,
1134
    pub step: u32, // TODO: ensure on deserialization it is > 0
1135
}
1136

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

1146
impl From<TimeStep> for geoengine_datatypes::primitives::TimeStep {
1147
    fn from(value: TimeStep) -> Self {
4✔
1148
        Self {
4✔
1149
            granularity: value.granularity.into(),
4✔
1150
            step: value.step,
4✔
1151
        }
4✔
1152
    }
4✔
1153
}
1154

1155
/// Stores time intervals in ms in close-open semantic [start, end)
1156
#[derive(Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ToSql, FromSql)]
40✔
1157
pub struct TimeInterval {
1158
    start: TimeInstance,
1159
    end: TimeInstance,
1160
}
1161

1162
impl<'a> ToSchema<'a> for TimeInterval {
1163
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
1164
        use utoipa::openapi::*;
2✔
1165
        (
2✔
1166
            "TimeInterval",
2✔
1167
            ObjectBuilder::new().schema_type(SchemaType::String).into(),
2✔
1168
        )
2✔
1169
    }
2✔
1170
}
1171

1172
impl From<TimeInterval> for geoengine_datatypes::primitives::TimeInterval {
1173
    fn from(value: TimeInterval) -> Self {
34✔
1174
        geoengine_datatypes::primitives::TimeInterval::new_unchecked::<
34✔
1175
            geoengine_datatypes::primitives::TimeInstance,
34✔
1176
            geoengine_datatypes::primitives::TimeInstance,
34✔
1177
        >(value.start.into(), value.end.into())
34✔
1178
    }
34✔
1179
}
1180

1181
impl From<geoengine_datatypes::primitives::TimeInterval> for TimeInterval {
1182
    fn from(value: geoengine_datatypes::primitives::TimeInterval) -> Self {
60✔
1183
        Self {
60✔
1184
            start: value.start().into(),
60✔
1185
            end: value.end().into(),
60✔
1186
        }
60✔
1187
    }
60✔
1188
}
1189

1190
impl core::fmt::Debug for TimeInterval {
1191
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
×
1192
        write!(
×
1193
            f,
×
1194
            "TimeInterval [{}, {})",
×
1195
            self.start.inner(),
×
1196
            &self.end.inner()
×
1197
        )
×
1198
    }
×
1199
}
1200

1201
#[derive(
1202
    Debug,
×
1203
    Ord,
×
1204
    PartialOrd,
×
1205
    Eq,
1206
    PartialEq,
×
1207
    Hash,
×
1208
    Deserialize,
8✔
1209
    Serialize,
5✔
1210
    Copy,
1211
    Clone,
×
1212
    ToSchema,
2✔
1213
    FromSql,
×
1214
    ToSql,
×
1215
)]
1216
pub enum RasterDataType {
1217
    U8,
1218
    U16,
1219
    U32,
1220
    U64,
1221
    I8,
1222
    I16,
1223
    I32,
1224
    I64,
1225
    F32,
1226
    F64,
1227
}
1228

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

1246
impl From<RasterDataType> for geoengine_datatypes::raster::RasterDataType {
1247
    fn from(value: RasterDataType) -> Self {
4✔
1248
        match value {
4✔
1249
            RasterDataType::U8 => Self::U8,
4✔
1250
            RasterDataType::U16 => Self::U16,
×
1251
            RasterDataType::U32 => Self::U32,
×
1252
            RasterDataType::U64 => Self::U64,
×
1253
            RasterDataType::I8 => Self::I8,
×
1254
            RasterDataType::I16 => Self::I16,
×
1255
            RasterDataType::I32 => Self::I32,
×
1256
            RasterDataType::I64 => Self::I64,
×
1257
            RasterDataType::F32 => Self::F32,
×
1258
            RasterDataType::F64 => Self::F64,
×
1259
        }
1260
    }
4✔
1261
}
1262

1263
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
×
1264
#[serde(rename_all = "UPPERCASE")]
1265
pub enum ResamplingMethod {
1266
    Nearest,
1267
    Average,
1268
    Bilinear,
1269
    Cubic,
1270
    CubicSpline,
1271
    Lanczos,
1272
}
1273

1274
impl std::fmt::Display for ResamplingMethod {
1275
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
1276
        match self {
×
1277
            ResamplingMethod::Nearest => write!(f, "NEAREST"),
×
1278
            ResamplingMethod::Average => write!(f, "AVERAGE"),
×
1279
            ResamplingMethod::Bilinear => write!(f, "BILINEAR"),
×
1280
            ResamplingMethod::Cubic => write!(f, "CUBIC"),
×
1281
            ResamplingMethod::CubicSpline => write!(f, "CUBICSPLINE"),
×
1282
            ResamplingMethod::Lanczos => write!(f, "LANCZOS"),
×
1283
        }
1284
    }
×
1285
}
1286

1287
impl From<ResamplingMethod> for geoengine_datatypes::util::gdal::ResamplingMethod {
1288
    fn from(value: ResamplingMethod) -> Self {
×
1289
        match value {
×
1290
            ResamplingMethod::Nearest => Self::Nearest,
×
1291
            ResamplingMethod::Average => Self::Average,
×
1292
            ResamplingMethod::Bilinear => Self::Bilinear,
×
1293
            ResamplingMethod::Cubic => Self::Cubic,
×
1294
            ResamplingMethod::CubicSpline => Self::CubicSpline,
×
1295
            ResamplingMethod::Lanczos => Self::Lanczos,
×
1296
        }
1297
    }
×
1298
}
1299

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

1304
// manual implementation utoipa generates an integer field
1305
impl<'a> ToSchema<'a> for RgbaColor {
1306
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
1307
        use utoipa::openapi::*;
2✔
1308
        (
2✔
1309
            "RgbaColor",
2✔
1310
            ArrayBuilder::new()
2✔
1311
                .items(ObjectBuilder::new().schema_type(SchemaType::Integer))
2✔
1312
                .min_items(Some(4))
2✔
1313
                .max_items(Some(4))
2✔
1314
                .into(),
2✔
1315
        )
2✔
1316
    }
2✔
1317
}
1318

1319
impl From<geoengine_datatypes::operations::image::RgbaColor> for RgbaColor {
1320
    fn from(color: geoengine_datatypes::operations::image::RgbaColor) -> Self {
×
1321
        Self(color.into_inner())
×
1322
    }
×
1323
}
1324

1325
impl From<RgbaColor> for geoengine_datatypes::operations::image::RgbaColor {
1326
    fn from(color: RgbaColor) -> Self {
×
1327
        Self::new(color.0[0], color.0[1], color.0[2], color.0[3])
×
1328
    }
×
1329
}
1330

1331
/// A container type for breakpoints that specify a value to color mapping
1332
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
×
1333
pub struct Breakpoint {
1334
    pub value: NotNanF64,
1335
    pub color: RgbaColor,
1336
}
1337

1338
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
×
1339
pub struct NotNanF64(NotNan<f64>);
1340

1341
impl From<NotNan<f64>> for NotNanF64 {
1342
    fn from(value: NotNan<f64>) -> Self {
×
1343
        Self(value)
×
1344
    }
×
1345
}
1346

1347
impl From<NotNanF64> for NotNan<f64> {
1348
    fn from(value: NotNanF64) -> Self {
×
1349
        value.0
×
1350
    }
×
1351
}
1352

1353
impl ToSql for NotNanF64 {
1354
    fn to_sql(
×
1355
        &self,
×
1356
        ty: &postgres_types::Type,
×
1357
        w: &mut bytes::BytesMut,
×
1358
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
×
1359
        <f64 as ToSql>::to_sql(&self.0.into_inner(), ty, w)
×
1360
    }
×
1361

1362
    fn accepts(ty: &postgres_types::Type) -> bool {
×
1363
        <f64 as ToSql>::accepts(ty)
×
1364
    }
×
1365

1366
    postgres_types::to_sql_checked!();
1367
}
1368

1369
impl<'a> FromSql<'a> for NotNanF64 {
1370
    fn from_sql(
×
1371
        ty: &postgres_types::Type,
×
1372
        raw: &'a [u8],
×
1373
    ) -> Result<NotNanF64, Box<dyn std::error::Error + Sync + Send>> {
×
1374
        let value = <f64 as FromSql>::from_sql(ty, raw)?;
×
1375

1376
        Ok(NotNanF64(value.try_into()?))
×
1377
    }
×
1378

1379
    fn accepts(ty: &postgres_types::Type) -> bool {
×
1380
        <f64 as FromSql>::accepts(ty)
×
1381
    }
×
1382
}
1383

1384
// manual implementation because of NotNan
1385
impl<'a> ToSchema<'a> for Breakpoint {
1386
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
1387
        use utoipa::openapi::*;
2✔
1388
        (
2✔
1389
            "Breakpoint",
2✔
1390
            ObjectBuilder::new()
2✔
1391
                .property("value", Object::with_type(SchemaType::Number))
2✔
1392
                .property("color", Ref::from_schema_name("RgbaColor"))
2✔
1393
                .into(),
2✔
1394
        )
2✔
1395
    }
2✔
1396
}
1397

1398
impl From<geoengine_datatypes::operations::image::Breakpoint> for Breakpoint {
1399
    fn from(breakpoint: geoengine_datatypes::operations::image::Breakpoint) -> Self {
×
1400
        Self {
×
1401
            value: breakpoint.value.into(),
×
1402
            color: breakpoint.color.into(),
×
1403
        }
×
1404
    }
×
1405
}
1406

1407
#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
2✔
1408
#[serde(untagged, rename_all = "camelCase", into = "OverUnderColors")]
1409
pub enum DefaultColors {
1410
    #[serde(rename_all = "camelCase")]
1411
    DefaultColor { default_color: RgbaColor },
1412
    #[serde(rename_all = "camelCase")]
1413
    OverUnder(OverUnderColors),
1414
}
1415

1416
#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
2✔
1417
#[serde(rename_all = "camelCase")]
1418
pub struct OverUnderColors {
1419
    pub over_color: RgbaColor,
1420
    pub under_color: RgbaColor,
1421
}
1422

1423
impl From<DefaultColors> for OverUnderColors {
1424
    fn from(value: DefaultColors) -> Self {
×
1425
        match value {
×
1426
            DefaultColors::DefaultColor { default_color } => Self {
×
1427
                over_color: default_color,
×
1428
                under_color: default_color,
×
1429
            },
×
1430
            DefaultColors::OverUnder(over_under) => over_under,
×
1431
        }
1432
    }
×
1433
}
1434

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

1452
impl From<geoengine_datatypes::operations::image::DefaultColors> for DefaultColors {
1453
    fn from(value: geoengine_datatypes::operations::image::DefaultColors) -> Self {
×
1454
        match value {
×
1455
            geoengine_datatypes::operations::image::DefaultColors::DefaultColor {
1456
                default_color,
×
1457
            } => Self::DefaultColor {
×
1458
                default_color: default_color.into(),
×
1459
            },
×
1460
            geoengine_datatypes::operations::image::DefaultColors::OverUnder {
1461
                over_color,
×
1462
                under_color,
×
1463
            } => Self::OverUnder(OverUnderColors {
×
1464
                over_color: over_color.into(),
×
1465
                under_color: under_color.into(),
×
1466
            }),
×
1467
        }
1468
    }
×
1469
}
1470

1471
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
2✔
1472
#[serde(rename_all = "camelCase")]
1473
pub struct LinearGradient {
1474
    pub breakpoints: Vec<Breakpoint>,
1475
    pub no_data_color: RgbaColor,
1476
    #[serde(flatten)]
1477
    pub color_fields: DefaultColors,
1478
}
1479

1480
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
2✔
1481
#[serde(rename_all = "camelCase")]
1482
pub struct LogarithmicGradient {
1483
    pub breakpoints: Vec<Breakpoint>,
1484
    pub no_data_color: RgbaColor,
1485
    #[serde(flatten)]
1486
    pub color_fields: DefaultColors,
1487
}
1488

1489
/// A colorizer specifies a mapping between raster values and an output image
1490
/// There are different variants that perform different kinds of mapping.
1491
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
2✔
1492
#[serde(rename_all = "camelCase", tag = "type")]
1493
pub enum Colorizer {
1494
    #[serde(rename_all = "camelCase")]
1495
    LinearGradient(LinearGradient),
1496
    #[serde(rename_all = "camelCase")]
1497
    LogarithmicGradient(LogarithmicGradient),
1498
    #[serde(rename_all = "camelCase")]
1499
    Palette {
1500
        colors: Palette,
1501
        no_data_color: RgbaColor,
1502
        default_color: RgbaColor,
1503
    },
1504
    Rgba,
1505
}
1506

1507
impl From<geoengine_datatypes::operations::image::Colorizer> for Colorizer {
1508
    fn from(v: geoengine_datatypes::operations::image::Colorizer) -> Self {
×
1509
        match v {
×
1510
            geoengine_datatypes::operations::image::Colorizer::LinearGradient {
1511
                breakpoints,
×
1512
                no_data_color,
×
1513
                default_colors: color_fields,
×
1514
            } => Self::LinearGradient(LinearGradient {
×
1515
                breakpoints: breakpoints
×
1516
                    .into_iter()
×
1517
                    .map(Into::into)
×
1518
                    .collect::<Vec<Breakpoint>>(),
×
1519
                no_data_color: no_data_color.into(),
×
1520
                color_fields: color_fields.into(),
×
1521
            }),
×
1522
            geoengine_datatypes::operations::image::Colorizer::LogarithmicGradient {
1523
                breakpoints,
×
1524
                no_data_color,
×
1525
                default_colors: color_fields,
×
1526
            } => Self::LogarithmicGradient(LogarithmicGradient {
×
1527
                breakpoints: breakpoints
×
1528
                    .into_iter()
×
1529
                    .map(Into::into)
×
1530
                    .collect::<Vec<Breakpoint>>(),
×
1531
                no_data_color: no_data_color.into(),
×
1532
                color_fields: color_fields.into(),
×
1533
            }),
×
1534
            geoengine_datatypes::operations::image::Colorizer::Palette {
1535
                colors,
×
1536
                no_data_color,
×
1537
                default_color,
×
1538
            } => Self::Palette {
×
1539
                colors: colors.into(),
×
1540
                no_data_color: no_data_color.into(),
×
1541
                default_color: default_color.into(),
×
1542
            },
×
1543
            geoengine_datatypes::operations::image::Colorizer::Rgba => Self::Rgba,
×
1544
        }
1545
    }
×
1546
}
1547

1548
/// A map from value to color
1549
///
1550
/// It is assumed that is has at least one and at most 256 entries.
1551
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
2✔
1552
#[serde(try_from = "SerializablePalette", into = "SerializablePalette")]
1553
pub struct Palette(pub HashMap<NotNan<f64>, RgbaColor>);
1554

1555
impl From<geoengine_datatypes::operations::image::Palette> for Palette {
1556
    fn from(palette: geoengine_datatypes::operations::image::Palette) -> Self {
×
1557
        Self(
×
1558
            palette
×
1559
                .into_inner()
×
1560
                .into_iter()
×
1561
                .map(|(value, color)| (value, color.into()))
×
1562
                .collect(),
×
1563
        )
×
1564
    }
×
1565
}
1566

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

1572
impl From<Palette> for SerializablePalette {
1573
    fn from(palette: Palette) -> Self {
×
1574
        Self(
×
1575
            palette
×
1576
                .0
×
1577
                .into_iter()
×
1578
                .map(|(k, v)| (k.to_string(), v))
×
1579
                .collect(),
×
1580
        )
×
1581
    }
×
1582
}
1583

1584
impl TryFrom<SerializablePalette> for Palette {
1585
    type Error = <NotNan<f64> as FromStr>::Err;
1586

1587
    fn try_from(palette: SerializablePalette) -> Result<Self, Self::Error> {
×
1588
        let mut inner = HashMap::<NotNan<f64>, RgbaColor>::with_capacity(palette.0.len());
×
1589
        for (k, v) in palette.0 {
×
1590
            inner.insert(k.parse()?, v);
×
1591
        }
1592
        Ok(Self(inner))
×
1593
    }
×
1594
}
1595

1596
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Hash, Eq, PartialOrd, Ord, ToSchema)]
2✔
1597
pub struct RasterPropertiesKey {
1598
    pub domain: Option<String>,
1599
    pub key: String,
1600
}
1601

1602
impl From<geoengine_datatypes::raster::RasterPropertiesKey> for RasterPropertiesKey {
1603
    fn from(value: geoengine_datatypes::raster::RasterPropertiesKey) -> Self {
×
1604
        Self {
×
1605
            domain: value.domain,
×
1606
            key: value.key,
×
1607
        }
×
1608
    }
×
1609
}
1610

1611
impl From<RasterPropertiesKey> for geoengine_datatypes::raster::RasterPropertiesKey {
1612
    fn from(value: RasterPropertiesKey) -> Self {
×
1613
        Self {
×
1614
            domain: value.domain,
×
1615
            key: value.key,
×
1616
        }
×
1617
    }
×
1618
}
1619

1620
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
2✔
1621
pub enum RasterPropertiesEntryType {
1622
    Number,
1623
    String,
1624
}
1625

1626
impl From<geoengine_datatypes::raster::RasterPropertiesEntryType> for RasterPropertiesEntryType {
1627
    fn from(value: geoengine_datatypes::raster::RasterPropertiesEntryType) -> Self {
×
1628
        match value {
×
1629
            geoengine_datatypes::raster::RasterPropertiesEntryType::Number => Self::Number,
×
1630
            geoengine_datatypes::raster::RasterPropertiesEntryType::String => Self::String,
×
1631
        }
1632
    }
×
1633
}
1634

1635
impl From<RasterPropertiesEntryType> for geoengine_datatypes::raster::RasterPropertiesEntryType {
1636
    fn from(value: RasterPropertiesEntryType) -> Self {
×
1637
        match value {
×
1638
            RasterPropertiesEntryType::Number => Self::Number,
×
1639
            RasterPropertiesEntryType::String => Self::String,
×
1640
        }
1641
    }
×
1642
}
1643

1644
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, ToSchema)]
28✔
1645
pub struct DateTimeParseFormat {
1646
    fmt: String,
1647
    has_tz: bool,
1648
    has_time: bool,
1649
}
1650

1651
impl From<geoengine_datatypes::primitives::DateTimeParseFormat> for DateTimeParseFormat {
1652
    fn from(value: geoengine_datatypes::primitives::DateTimeParseFormat) -> Self {
4✔
1653
        Self {
4✔
1654
            fmt: value._to_parse_format().to_string(),
4✔
1655
            has_tz: value.has_tz(),
4✔
1656
            has_time: value.has_time(),
4✔
1657
        }
4✔
1658
    }
4✔
1659
}
1660

1661
impl From<DateTimeParseFormat> for geoengine_datatypes::primitives::DateTimeParseFormat {
1662
    fn from(value: DateTimeParseFormat) -> Self {
4✔
1663
        Self::custom(value.fmt)
4✔
1664
    }
4✔
1665
}
1666

1667
impl DateTimeParseFormat {
1668
    // this is used as default value
1669
    pub fn unix() -> Self {
×
1670
        geoengine_datatypes::primitives::DateTimeParseFormat::unix().into()
×
1671
    }
×
1672
}
1673

1674
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
2✔
1675
pub struct NoGeometry;
1676

1677
impl From<geoengine_datatypes::primitives::NoGeometry> for NoGeometry {
1678
    fn from(_: geoengine_datatypes::primitives::NoGeometry) -> Self {
×
1679
        Self {}
×
1680
    }
×
1681
}
1682

1683
impl From<NoGeometry> for geoengine_datatypes::primitives::NoGeometry {
1684
    fn from(_: NoGeometry) -> Self {
×
1685
        Self {}
×
1686
    }
×
1687
}
1688

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

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

1702
impl From<MultiPoint> for geoengine_datatypes::primitives::MultiPoint {
1703
    fn from(value: MultiPoint) -> Self {
×
1704
        Self::new(value.coordinates.into_iter().map(Into::into).collect()).unwrap()
×
1705
    }
×
1706
}
1707

1708
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1709
pub struct MultiLineString {
1710
    pub coordinates: Vec<Vec<Coordinate2D>>,
1711
}
1712

1713
impl From<geoengine_datatypes::primitives::MultiLineString> for MultiLineString {
1714
    fn from(value: geoengine_datatypes::primitives::MultiLineString) -> Self {
×
1715
        Self {
×
1716
            coordinates: value
×
1717
                .lines()
×
1718
                .iter()
×
1719
                .map(|x| x.iter().map(|x| (*x).into()).collect())
×
1720
                .collect(),
×
1721
        }
×
1722
    }
×
1723
}
1724

1725
impl From<MultiLineString> for geoengine_datatypes::primitives::MultiLineString {
1726
    fn from(value: MultiLineString) -> Self {
×
1727
        Self::new(
×
1728
            value
×
1729
                .coordinates
×
1730
                .into_iter()
×
1731
                .map(|x| x.into_iter().map(Into::into).collect())
×
1732
                .collect(),
×
1733
        )
×
1734
        .unwrap()
×
1735
    }
×
1736
}
1737

1738
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1739
pub struct MultiPolygon {
1740
    pub polygons: Vec<Vec<Vec<Coordinate2D>>>,
1741
}
1742

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

1759
impl From<MultiPolygon> for geoengine_datatypes::primitives::MultiPolygon {
1760
    fn from(value: MultiPolygon) -> Self {
×
1761
        Self::new(
×
1762
            value
×
1763
                .polygons
×
1764
                .iter()
×
1765
                .map(|x| {
×
1766
                    x.iter()
×
1767
                        .map(|y| y.iter().map(|y| (*y).into()).collect())
×
1768
                        .collect()
×
1769
                })
×
1770
                .collect(),
×
1771
        )
×
1772
        .unwrap()
×
1773
    }
×
1774
}
1775

1776
#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)]
4✔
1777
pub struct StringPair((String, String));
1778

1779
impl<'a> ToSchema<'a> for StringPair {
1780
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
6✔
1781
        use utoipa::openapi::*;
6✔
1782
        (
6✔
1783
            "StringPair",
6✔
1784
            ArrayBuilder::new()
6✔
1785
                .items(Object::with_type(SchemaType::String))
6✔
1786
                .min_items(Some(2))
6✔
1787
                .max_items(Some(2))
6✔
1788
                .into(),
6✔
1789
        )
6✔
1790
    }
6✔
1791
}
1792

1793
impl From<(String, String)> for StringPair {
1794
    fn from(value: (String, String)) -> Self {
26✔
1795
        Self(value)
26✔
1796
    }
26✔
1797
}
1798

1799
impl From<StringPair> for (String, String) {
1800
    fn from(value: StringPair) -> Self {
×
1801
        value.0
×
1802
    }
×
1803
}
1804

1805
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize, ToSchema)]
2✔
1806
pub enum PlotOutputFormat {
1807
    JsonPlain,
1808
    JsonVega,
1809
    ImagePng,
1810
}
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