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

geo-engine / geoengine / 18554766227

16 Oct 2025 08:12AM UTC coverage: 88.843% (+0.3%) from 88.543%
18554766227

push

github

web-flow
build: update dependencies (#1081)

* update sqlfluff

* clippy autofix

* manual clippy fixes

* removal of unused code

* update deps

* upgrade packages

* enable cargo lints

* make sqlfluff happy

* fix chrono parsin error

* clippy

* byte_size

* fix image cmp with tiffs

* remove debug

177 of 205 new or added lines in 38 files covered. (86.34%)

41 existing lines in 20 files now uncovered.

106415 of 119779 relevant lines covered (88.84%)

84190.21 hits per line

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

80.35
/expression/src/functions.rs
1
use crate::{
2
    codegen::{DataType, Identifier},
3
    error::ExpressionSemanticError,
4
};
5
use proc_macro2::TokenStream;
6
use quote::{ToTokens, quote};
7
use std::{collections::HashMap, hash::Hash, sync::OnceLock};
8

9
type Result<T, E = ExpressionSemanticError> = std::result::Result<T, E>;
10

11
/// A function generator that can be used to generate a function
12
/// that can be used in an expression.
13
///
14
/// It checks whether the input arguments are valid.
15
///
16
pub struct FunctionGenerator {
17
    /// User-facing name of the function
18
    name: &'static str,
19

20
    /// Method to validate the input arguments.
21
    /// Return true if the arguments are valid.
22
    generate_fn: fn(&str, &[DataType]) -> Result<Function>,
23
}
24

25
impl FunctionGenerator {
26
    pub fn generate(&self, args: &[DataType]) -> Result<Function> {
81✔
27
        (self.generate_fn)(self.name, args)
81✔
28
    }
81✔
29
}
30

31
#[derive(Debug, Clone, Eq)]
32
pub struct Function {
33
    /// The call name of the function in the expression src code
34
    name: Identifier,
35

36
    /// Function signature
37
    signature: Vec<DataType>,
38

39
    /// Defines the output [`DataType`] of this function
40
    output_type: DataType,
41

42
    /// Write the function to a token stream
43
    token_fn: fn(&Self, &mut TokenStream) -> (),
44
}
45

46
impl Function {
47
    pub fn name(&self) -> &Identifier {
79✔
48
        &self.name
79✔
49
    }
79✔
50

51
    pub fn output_type(&self) -> DataType {
83✔
52
        self.output_type
83✔
53
    }
83✔
54
}
55

56
impl ToTokens for Function {
57
    fn to_tokens(&self, tokens: &mut TokenStream) {
62✔
58
        (self.token_fn)(self, tokens);
62✔
59
    }
62✔
60
}
61

62
/// We generate a unique name, so we can compare [`Function`]s by name.
63
mod cmp_by_name {
64
    use super::*;
65

66
    impl PartialEq for Function {
67
        fn eq(&self, other: &Self) -> bool {
×
68
            self.name == other.name
×
69
        }
×
70
    }
71

72
    impl Ord for Function {
73
        fn cmp(&self, other: &Self) -> std::cmp::Ordering {
50✔
74
            self.name.cmp(&other.name)
50✔
75
        }
50✔
76
    }
77

78
    impl PartialOrd for Function {
79
        fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
×
NEW
80
            Some(self.cmp(other))
×
81
        }
×
82
    }
83

84
    impl Hash for Function {
85
        fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
×
86
            self.name.hash(state);
×
87
        }
×
88
    }
89
}
90

91
pub static FUNCTIONS: OnceLock<HashMap<&'static str, FunctionGenerator>> = OnceLock::new();
92

93
/// Add a function generator for a function that returns a [`DataType::Number`] constant.
94
macro_rules! add_const_num {
95
    ( $name:literal, $functions:expr, $fn:ty ) => {{
96
        let name = $name;
97
        $functions.insert(
98
            name,
99
            FunctionGenerator {
100
                name,
101
                generate_fn: |name, args| match args {
4✔
102
                    [] => Ok(Function {
4✔
103
                        name: unique_name(name, args),
4✔
104
                        signature: vec![DataType::Number, DataType::Number],
4✔
105
                        output_type: DataType::Number,
4✔
106
                        token_fn: |fn_, tokens| {
4✔
107
                            let name = &fn_.name;
4✔
108
                            let dtype = DataType::Number;
4✔
109
                            tokens.extend(quote! {
4✔
110
                                fn #name() -> Option<#dtype> {
111
                                    Some($fn)
112
                                }
113
                            });
114
                        },
4✔
115
                    }),
116
                    _ => Err(ExpressionSemanticError::InvalidFunctionArguments {
×
117
                        name: name.into(),
×
118
                        expected: vec![DataType::Number.group_name().to_string()],
×
119
                        actual: args.into(),
×
120
                    }),
×
121
                },
4✔
122
            },
123
        );
124
    }};
125
}
126

127
/// Add a function generator for a function with 1 [`DataType::Number`] that returns a [`DataType::Number`].
128
macro_rules! add_1_num {
129
    ( $name:literal, $functions:expr, $fn:ty ) => {{
130
        let name = $name;
131
        $functions.insert(
132
            name,
133
            FunctionGenerator {
134
                name,
135
                generate_fn: |name, args| match args {
15✔
136
                    [DataType::Number] => Ok(Function {
15✔
137
                        name: unique_name(name, args),
14✔
138
                        signature: vec![DataType::Number, DataType::Number],
14✔
139
                        output_type: DataType::Number,
14✔
140
                        token_fn: |fn_, tokens| {
14✔
141
                            let name = &fn_.name;
14✔
142
                            let dtype = DataType::Number;
14✔
143
                            tokens.extend(quote! {
14✔
144
                                fn #name(a: Option<#dtype>) -> Option<#dtype> {
145
                                    a.map($fn)
146
                                }
147
                            });
148
                        },
14✔
149
                    }),
150
                    _ => Err(ExpressionSemanticError::InvalidFunctionArguments {
1✔
151
                        name: name.into(),
1✔
152
                        expected: vec![DataType::Number.group_name().to_string()],
1✔
153
                        actual: args.into(),
1✔
154
                    }),
1✔
155
                },
15✔
156
            },
157
        );
158
    }};
159
}
160

161
/// Add a function generator for a function with 2 [`DataType::Number`]s that returns a [`DataType::Number`].
162
macro_rules! add_2_num {
163
    ( $name:literal, $functions:expr, $fn:ty ) => {{
164
        let name = $name;
165
        $functions.insert(
166
            name,
167
            FunctionGenerator {
168
                name,
169
                generate_fn: |name, args| match args {
56✔
170
                    [DataType::Number, DataType::Number] => Ok(Function {
57✔
171
                        name: unique_name(name, args),
56✔
172
                        signature: vec![DataType::Number, DataType::Number],
56✔
173
                        output_type: DataType::Number,
56✔
174
                        token_fn: |fn_, tokens| {
40✔
175
                            let name = &fn_.name;
40✔
176
                            let dtype = DataType::Number;
40✔
177
                            tokens.extend(quote! {
40✔
178
                                fn #name(a: Option<#dtype>, b: Option<#dtype>) -> Option<#dtype> {
179
                                    match (a, b) {
180
                                        (Some(a), Some(b)) => Some($fn(a, b)),
181
                                        _ => None,
182
                                    }
183
                                }
184
                            });
185
                        },
40✔
186
                    }),
187
                    _ => Err(ExpressionSemanticError::InvalidFunctionArguments {
1✔
188
                        name: name.into(),
1✔
189
                        expected: [DataType::Number, DataType::Number]
1✔
190
                            .iter()
1✔
191
                            .map(DataType::group_name)
1✔
192
                            .map(ToString::to_string)
1✔
193
                            .collect(),
1✔
194
                        actual: args.into(),
1✔
195
                    }),
1✔
196
                },
57✔
197
            },
198
        );
199
    }};
200
}
201

202
// TODO: change to [`std::sync::LazyLock'] once stable
203
#[allow(clippy::too_many_lines)]
204
pub fn init_functions() -> HashMap<&'static str, FunctionGenerator> {
2✔
205
    let mut functions = HashMap::new();
2✔
206

207
    add_2_num!("add", functions, std::ops::Add::add);
2✔
208
    add_2_num!("sub", functions, std::ops::Sub::sub);
2✔
209
    add_2_num!("mul", functions, std::ops::Mul::mul);
2✔
210
    add_2_num!("div", functions, std::ops::Div::div);
2✔
211
    add_2_num!("min", functions, f64::min);
2✔
212
    add_2_num!("max", functions, f64::max);
2✔
213
    add_2_num!("abs", functions, f64::abs);
2✔
214
    add_2_num!("pow", functions, f64::powf);
2✔
215
    add_2_num!("mod", functions, std::ops::Rem::rem);
2✔
216

217
    add_1_num!("sqrt", functions, f64::sqrt);
2✔
218
    add_1_num!("cos", functions, f64::cos);
2✔
219
    add_1_num!("sin", functions, f64::sin);
2✔
220
    add_1_num!("tan", functions, f64::tan);
2✔
221
    add_1_num!("acos", functions, f64::acos);
2✔
222
    add_1_num!("asin", functions, f64::asin);
2✔
223
    add_1_num!("atan", functions, f64::atan);
2✔
224
    add_1_num!("log10", functions, f64::log10);
2✔
225
    add_1_num!("ln", functions, f64::ln);
2✔
226
    add_1_num!("round", functions, f64::round);
2✔
227
    add_1_num!("ceil", functions, f64::ceil);
2✔
228
    add_1_num!("floor", functions, f64::floor);
2✔
229
    add_1_num!("to_radians", functions, f64::to_radians);
2✔
230
    add_1_num!("to_degrees", functions, f64::to_degrees);
2✔
231
    add_1_num!("tanh", functions, f64::tanh);
2✔
232

233
    add_const_num!("pi", functions, std::f64::consts::PI);
2✔
234
    add_const_num!("e", functions, std::f64::consts::E);
2✔
235

236
    // [`geo`] functions
237

238
    let name = "centroid";
2✔
239
    functions.insert(
2✔
240
        name,
2✔
241
        FunctionGenerator {
242
            name,
2✔
243
            generate_fn: |name, args| match args {
4✔
244
                [
245
                    dtype @ (DataType::MultiPoint
4✔
246
                    | DataType::MultiLineString
247
                    | DataType::MultiPolygon),
248
                ] => Ok(Function {
249
                    name: unique_name(name, args),
4✔
250
                    signature: vec![*dtype],
4✔
251
                    output_type: DataType::MultiPoint,
4✔
252
                    token_fn: move |fn_, tokens| {
3✔
253
                        let name = &fn_.name;
3✔
254
                        let dtype = fn_.signature[0];
3✔
255

256
                        let inner_operation = match dtype {
3✔
257
                            DataType::MultiPoint
258
                            | DataType::MultiLineString
259
                            | DataType::MultiPolygon => quote! {
3✔
260
                                geom.centroid()
261
                            },
262
                            // should never happen
263
                            DataType::Number => quote! {},
×
264
                        };
265

266
                        let output_type = &fn_.output_type;
3✔
267

268
                        tokens.extend(quote! {
3✔
269
                            fn #name(geom: Option<#dtype>) -> Option<#output_type> {
270
                                #inner_operation
271
                            }
272
                        });
273
                    },
3✔
274
                }),
275
                _ => Err(ExpressionSemanticError::InvalidFunctionArguments {
×
276
                    name: name.into(),
×
277
                    expected: [DataType::MultiPoint]
×
278
                        .iter()
×
279
                        .map(DataType::group_name)
×
280
                        .map(ToString::to_string)
×
281
                        .collect(),
×
282
                    actual: args.into(),
×
283
                }),
×
284
            },
4✔
285
        },
286
    );
287

288
    let name = "area";
2✔
289
    functions.insert(
2✔
290
        name,
2✔
291
        FunctionGenerator {
292
            name,
2✔
293
            generate_fn: |name, args| match args {
1✔
294
                [
295
                    dtype @ (DataType::MultiPoint
1✔
296
                    | DataType::MultiLineString
297
                    | DataType::MultiPolygon),
298
                ] => Ok(Function {
299
                    name: unique_name(name, args),
1✔
300
                    signature: vec![*dtype],
1✔
301
                    output_type: DataType::Number,
1✔
302
                    token_fn: move |fn_, tokens| {
1✔
303
                        let name = &fn_.name;
1✔
304
                        let dtype = fn_.signature[0];
1✔
305

306
                        let inner_operation = match dtype {
1✔
307
                            DataType::MultiPoint
308
                            | DataType::MultiLineString
309
                            | DataType::MultiPolygon => quote! {
1✔
310
                                geom.area()
311
                            },
312
                            // should never happen
313
                            DataType::Number => quote! {},
×
314
                        };
315

316
                        let output_type = &fn_.output_type;
1✔
317

318
                        tokens.extend(quote! {
1✔
319
                            fn #name(geom: Option<#dtype>) -> Option<#output_type> {
320
                                #inner_operation
321
                            }
322
                        });
323
                    },
1✔
324
                }),
325
                _ => Err(ExpressionSemanticError::InvalidFunctionArguments {
×
326
                    name: name.into(),
×
327
                    expected: [DataType::MultiPoint]
×
328
                        .iter()
×
329
                        .map(DataType::group_name)
×
330
                        .map(ToString::to_string)
×
331
                        .collect(),
×
332
                    actual: args.into(),
×
333
                }),
×
334
            },
1✔
335
        },
336
    );
337

338
    functions
2✔
339
}
2✔
340

341
pub const FUNCTION_PREFIX: &str = "expression_fn_";
342

343
/// Creates a unique name for a function to be used in the source code.
344
///
345
/// Format: `expression_fn__<name>__<dtype1>_<dtype2>_<dtype3>…`
346
///
347
fn unique_name(name: &str, signature: &[DataType]) -> Identifier {
79✔
348
    let mut fn_name = String::new();
79✔
349
    fn_name.push_str(FUNCTION_PREFIX);
79✔
350
    fn_name.push_str(name);
79✔
351
    fn_name.push('_');
79✔
352
    for dtype in signature {
210✔
353
        fn_name.push('_');
131✔
354
        fn_name.push(dtype.call_name_suffix());
131✔
355
    }
131✔
356
    fn_name.into()
79✔
357
}
79✔
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