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

geo-engine / geoengine / 3929938005

pending completion
3929938005

push

github

GitHub
Merge #713

84930 of 96741 relevant lines covered (87.79%)

79640.1 hits per line

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

93.28
/operators/src/util/input/string_or_number_range.rs
1
use std::{convert::TryFrom, ops::RangeInclusive};
2

3
use crate::error;
4
use crate::util::input::StringOrNumber;
5
use crate::util::Result;
6
use geoengine_datatypes::primitives::FeatureDataValue;
7
use num_traits::AsPrimitive;
8
use serde::de::{Error, SeqAccess, Visitor};
9
use serde::ser::SerializeTuple;
10
use serde::{Deserialize, Deserializer, Serialize, Serializer};
11

12
/// A type that allows use inputs to be either ranges of strings or numbers.
13
/// The range is inclusive.
14
/// TODO: generify for `RangeBounds`
15
#[derive(Debug, Clone, PartialEq)]
24✔
16
pub enum StringOrNumberRange {
17
    String(RangeInclusive<String>),
18
    Float(RangeInclusive<f64>),
19
    Int(RangeInclusive<i64>),
20
}
21

22
impl StringOrNumberRange {
23
    pub fn into_float_range(self) -> Result<Self> {
1✔
24
        RangeInclusive::<f64>::try_from(self).map(Into::into)
1✔
25
    }
1✔
26

27
    pub fn into_int_range(self) -> Result<Self> {
×
28
        RangeInclusive::<i64>::try_from(self).map(Into::into)
×
29
    }
×
30

31
    pub fn into_string_range(self) -> Result<Self> {
×
32
        RangeInclusive::<String>::try_from(self).map(Into::into)
×
33
    }
×
34
}
35

36
impl Serialize for StringOrNumberRange {
37
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
4✔
38
    where
4✔
39
        S: Serializer,
4✔
40
    {
4✔
41
        let mut tuple_serializer = serializer.serialize_tuple(2)?;
4✔
42

43
        match self {
4✔
44
            Self::String(range) => {
1✔
45
                tuple_serializer.serialize_element(range.start())?;
1✔
46
                tuple_serializer.serialize_element(range.end())?;
1✔
47
            }
48
            Self::Float(range) => {
1✔
49
                tuple_serializer.serialize_element(range.start())?;
1✔
50
                tuple_serializer.serialize_element(range.end())?;
1✔
51
            }
52
            Self::Int(range) => {
2✔
53
                tuple_serializer.serialize_element(range.start())?;
2✔
54
                tuple_serializer.serialize_element(range.end())?;
2✔
55
            }
56
        }
57

58
        tuple_serializer.end()
4✔
59
    }
4✔
60
}
61

62
impl<'de> Deserialize<'de> for StringOrNumberRange {
63
    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
7✔
64
    where
7✔
65
        D: Deserializer<'de>,
7✔
66
    {
7✔
67
        deserializer.deserialize_tuple(2, StringOrNumberRangeDeserializer)
7✔
68
    }
7✔
69
}
70

71
struct StringOrNumberRangeDeserializer;
72
impl<'de> Visitor<'de> for StringOrNumberRangeDeserializer {
73
    type Value = StringOrNumberRange;
74

75
    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
1✔
76
        formatter.write_str("a 2-tuple of integers, floats or strings")
1✔
77
    }
1✔
78

79
    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
7✔
80
    where
7✔
81
        A: SeqAccess<'de>,
7✔
82
    {
7✔
83
        let mut elements: Vec<StringOrNumber> = Vec::with_capacity(seq.size_hint().unwrap_or(2));
7✔
84

85
        while let Some(element) = seq.next_element()? {
21✔
86
            elements.push(element);
14✔
87
        }
14✔
88

89
        if elements.len() != 2 {
7✔
90
            return Err(A::Error::invalid_length(elements.len(), &Self));
×
91
        }
7✔
92

7✔
93
        let mut elements = elements.into_iter();
7✔
94
        let (from, to) = (
7✔
95
            elements.next().expect("checked"),
7✔
96
            elements.next().expect("checked"),
7✔
97
        );
7✔
98

7✔
99
        Ok(match (from, to) {
7✔
100
            (StringOrNumber::String(v1), StringOrNumber::String(v2)) => {
1✔
101
                StringOrNumberRange::String(v1..=v2)
1✔
102
            }
103
            (StringOrNumber::Float(v1), StringOrNumber::Float(v2)) => {
1✔
104
                StringOrNumberRange::Float(v1..=v2)
1✔
105
            }
106
            (StringOrNumber::Int(v1), StringOrNumber::Int(v2)) => StringOrNumberRange::Int(v1..=v2),
2✔
107
            (StringOrNumber::Float(v1), StringOrNumber::Int(v2)) => {
1✔
108
                StringOrNumberRange::Float(v1..=v2.as_())
1✔
109
            }
110
            (StringOrNumber::Int(v1), StringOrNumber::Float(v2)) => {
1✔
111
                StringOrNumberRange::Float(v1.as_()..=v2)
1✔
112
            }
113
            _ => {
114
                return Err(A::Error::invalid_type(
1✔
115
                    serde::de::Unexpected::Other("mismatched type"),
1✔
116
                    &Self,
1✔
117
                ));
1✔
118
            }
119
        })
120
    }
7✔
121
}
122

123
impl From<RangeInclusive<f64>> for StringOrNumberRange {
124
    fn from(v: RangeInclusive<f64>) -> Self {
1✔
125
        StringOrNumberRange::Float(v)
1✔
126
    }
1✔
127
}
128

129
impl From<RangeInclusive<i64>> for StringOrNumberRange {
130
    fn from(v: RangeInclusive<i64>) -> Self {
2✔
131
        StringOrNumberRange::Int(v)
2✔
132
    }
2✔
133
}
134

135
impl From<RangeInclusive<String>> for StringOrNumberRange {
136
    fn from(v: RangeInclusive<String>) -> Self {
2✔
137
        StringOrNumberRange::String(v)
2✔
138
    }
2✔
139
}
140

141
impl From<RangeInclusive<&str>> for StringOrNumberRange {
142
    fn from(v: RangeInclusive<&str>) -> Self {
1✔
143
        StringOrNumberRange::from((*v.start()).to_string()..=(*v.end()).to_string())
1✔
144
    }
1✔
145
}
146

147
impl TryFrom<StringOrNumberRange> for RangeInclusive<f64> {
148
    type Error = error::Error;
149

150
    fn try_from(value: StringOrNumberRange) -> Result<Self, Self::Error> {
6✔
151
        match value {
6✔
152
            StringOrNumberRange::String(_) => Err(error::Error::InvalidType {
1✔
153
                expected: "number".to_string(),
1✔
154
                found: "string".to_string(),
1✔
155
            }),
1✔
156
            StringOrNumberRange::Float(v) => Ok(v),
2✔
157
            StringOrNumberRange::Int(v) => Ok(v.start().as_()..=v.end().as_()),
3✔
158
        }
159
    }
6✔
160
}
161

162
impl TryFrom<&StringOrNumberRange> for RangeInclusive<f64> {
163
    type Error = error::Error;
164

165
    fn try_from(value: &StringOrNumberRange) -> Result<Self, Self::Error> {
2✔
166
        Self::try_from(value.clone())
2✔
167
    }
2✔
168
}
169

170
impl TryFrom<StringOrNumberRange> for RangeInclusive<i64> {
171
    type Error = error::Error;
172

173
    fn try_from(value: StringOrNumberRange) -> Result<Self, Self::Error> {
5✔
174
        match value {
5✔
175
            StringOrNumberRange::String(_) => Err(error::Error::InvalidType {
1✔
176
                expected: "number".to_string(),
1✔
177
                found: "string".to_string(),
1✔
178
            }),
1✔
179
            StringOrNumberRange::Float(v) => Ok(v.start().as_()..=v.end().as_()),
2✔
180
            StringOrNumberRange::Int(v) => Ok(v),
2✔
181
        }
182
    }
5✔
183
}
184

185
impl TryFrom<&StringOrNumberRange> for RangeInclusive<i64> {
186
    type Error = error::Error;
187

188
    fn try_from(value: &StringOrNumberRange) -> Result<Self, Self::Error> {
2✔
189
        Self::try_from(value.clone())
2✔
190
    }
2✔
191
}
192

193
impl TryFrom<StringOrNumberRange> for RangeInclusive<String> {
194
    type Error = error::Error;
195

196
    fn try_from(value: StringOrNumberRange) -> Result<Self, Self::Error> {
5✔
197
        match value {
5✔
198
            StringOrNumberRange::String(v) => Ok(v),
3✔
199
            StringOrNumberRange::Float(_) | StringOrNumberRange::Int(_) => {
200
                Err(error::Error::InvalidType {
2✔
201
                    expected: "string".to_string(),
2✔
202
                    found: "number".to_string(),
2✔
203
                })
2✔
204
            }
205
        }
206
    }
5✔
207
}
208

209
impl TryFrom<&StringOrNumberRange> for RangeInclusive<String> {
210
    type Error = error::Error;
211

212
    fn try_from(value: &StringOrNumberRange) -> Result<Self, Self::Error> {
1✔
213
        Self::try_from(value.clone())
1✔
214
    }
1✔
215
}
216

217
impl From<StringOrNumberRange> for RangeInclusive<FeatureDataValue> {
218
    fn from(value: StringOrNumberRange) -> Self {
1✔
219
        match value {
1✔
220
            StringOrNumberRange::String(v) => {
×
221
                let (start, end) = v.into_inner();
×
222
                FeatureDataValue::Text(start)..=FeatureDataValue::Text(end)
×
223
            }
224
            StringOrNumberRange::Float(v) => {
1✔
225
                let (start, end) = v.into_inner();
1✔
226
                FeatureDataValue::Float(start)..=FeatureDataValue::Float(end)
1✔
227
            }
228
            StringOrNumberRange::Int(v) => {
×
229
                let (start, end) = v.into_inner();
×
230
                FeatureDataValue::Int(start)..=FeatureDataValue::Int(end)
×
231
            }
232
        }
233
    }
1✔
234
}
235

236
impl From<&StringOrNumberRange> for RangeInclusive<FeatureDataValue> {
237
    fn from(value: &StringOrNumberRange) -> Self {
×
238
        Self::from(value.clone())
×
239
    }
×
240
}
241

242
#[cfg(test)]
243
mod tests {
244
    use super::*;
245

246
    #[test]
1✔
247
    fn serialize() {
1✔
248
        assert_eq!(
1✔
249
            serde_json::to_string(&StringOrNumberRange::String(
1✔
250
                "foo".to_string()..="bar".to_string()
1✔
251
            ))
1✔
252
            .unwrap(),
1✔
253
            "[\"foo\",\"bar\"]"
1✔
254
        );
1✔
255

256
        assert_eq!(
1✔
257
            serde_json::to_string(&StringOrNumberRange::Float(1337. ..=1338.)).unwrap(),
1✔
258
            "[1337.0,1338.0]"
1✔
259
        );
1✔
260

261
        assert_eq!(
1✔
262
            serde_json::to_string(&StringOrNumberRange::Int(42..=43)).unwrap(),
1✔
263
            "[42,43]"
1✔
264
        );
1✔
265
    }
1✔
266

267
    #[test]
1✔
268
    fn deserialize() {
1✔
269
        assert_eq!(
1✔
270
            serde_json::from_str::<StringOrNumberRange>("[\"foo\",\"bar\"]").unwrap(),
1✔
271
            StringOrNumberRange::String("foo".to_string()..="bar".to_string())
1✔
272
        );
1✔
273

274
        assert_eq!(
1✔
275
            serde_json::from_str::<StringOrNumberRange>("[1337.0,1338.0]").unwrap(),
1✔
276
            StringOrNumberRange::Float(1337. ..=1338.)
1✔
277
        );
1✔
278

279
        assert_eq!(
1✔
280
            serde_json::from_str::<StringOrNumberRange>("[42,43]").unwrap(),
1✔
281
            StringOrNumberRange::Int(42..=43)
1✔
282
        );
1✔
283

284
        assert_eq!(
1✔
285
            serde_json::from_str::<StringOrNumberRange>("[1337.0,1338]").unwrap(),
1✔
286
            StringOrNumberRange::Float(1337. ..=1338.)
1✔
287
        );
1✔
288

289
        assert_eq!(
1✔
290
            serde_json::from_str::<StringOrNumberRange>("[1337,1338.0]").unwrap(),
1✔
291
            StringOrNumberRange::Float(1337. ..=1338.)
1✔
292
        );
1✔
293

294
        assert!(serde_json::from_str::<StringOrNumberRange>("[\"foo\",42]").is_err());
1✔
295
    }
1✔
296

297
    #[test]
1✔
298
    #[allow(clippy::float_cmp)]
299
    fn try_into() {
1✔
300
        assert_eq!(
1✔
301
            RangeInclusive::<String>::try_from(StringOrNumberRange::String(
1✔
302
                "foo".to_string()..="bar".to_string()
1✔
303
            ))
1✔
304
            .unwrap(),
1✔
305
            "foo".to_string()..="bar".to_string()
1✔
306
        );
1✔
307
        assert_eq!(
1✔
308
            RangeInclusive::<String>::try_from(&StringOrNumberRange::String(
1✔
309
                "foo".to_string()..="bar".to_string()
1✔
310
            ))
1✔
311
            .unwrap(),
1✔
312
            "foo".to_string()..="bar".to_string()
1✔
313
        );
1✔
314
        assert_eq!(
1✔
315
            RangeInclusive::<String>::try_from(StringOrNumberRange::from("foo"..="bar")).unwrap(),
1✔
316
            "foo".to_string()..="bar".to_string()
1✔
317
        );
1✔
318

319
        assert_eq!(
1✔
320
            RangeInclusive::<f64>::try_from(StringOrNumberRange::Float(1337. ..=1338.)).unwrap(),
1✔
321
            1337. ..=1338.
1✔
322
        );
1✔
323
        assert_eq!(
1✔
324
            RangeInclusive::<f64>::try_from(&StringOrNumberRange::Float(1337. ..=1338.)).unwrap(),
1✔
325
            1337. ..=1338.
1✔
326
        );
1✔
327

328
        assert_eq!(
1✔
329
            RangeInclusive::<f64>::try_from(StringOrNumberRange::Int(1337..=1338)).unwrap(),
1✔
330
            1337. ..=1338.
1✔
331
        );
1✔
332
        assert_eq!(
1✔
333
            RangeInclusive::<f64>::try_from(&StringOrNumberRange::Int(1337..=1338)).unwrap(),
1✔
334
            1337. ..=1338.
1✔
335
        );
1✔
336

337
        assert_eq!(
1✔
338
            RangeInclusive::<i64>::try_from(StringOrNumberRange::Int(42..=43)).unwrap(),
1✔
339
            42..=43
1✔
340
        );
1✔
341
        assert_eq!(
1✔
342
            RangeInclusive::<i64>::try_from(&StringOrNumberRange::Int(42..=43)).unwrap(),
1✔
343
            42..=43
1✔
344
        );
1✔
345

346
        assert_eq!(
1✔
347
            RangeInclusive::<i64>::try_from(StringOrNumberRange::Float(42. ..=43.)).unwrap(),
1✔
348
            42..=43
1✔
349
        );
1✔
350
        assert_eq!(
1✔
351
            RangeInclusive::<i64>::try_from(&StringOrNumberRange::Float(42. ..=43.)).unwrap(),
1✔
352
            42..=43
1✔
353
        );
1✔
354

355
        assert!(
1✔
356
            RangeInclusive::<String>::try_from(StringOrNumberRange::Float(1337. ..=1338.)).is_err()
1✔
357
        );
1✔
358
        assert!(RangeInclusive::<String>::try_from(StringOrNumberRange::Int(42..=43)).is_err());
1✔
359

360
        assert!(RangeInclusive::<i64>::try_from(StringOrNumberRange::String(
1✔
361
            "foo".to_string()..="bar".to_string()
1✔
362
        ))
1✔
363
        .is_err());
1✔
364
        assert!(RangeInclusive::<f64>::try_from(StringOrNumberRange::String(
1✔
365
            "foo".to_string()..="bar".to_string()
1✔
366
        ))
1✔
367
        .is_err());
1✔
368
    }
1✔
369
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc