Skip to main content

brooks_lib/mel/
types.rs

1// brooks, Copyright 2026, Will Hawkins
2//
3// This file is part of brooks.
4
5// This file is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <https://www.gnu.org/licenses/>.
17
18//! MEL types
19
20use std::{
21    collections::HashMap,
22    fmt::{Debug, Display},
23};
24
25use brooks_macros::builtin_function;
26use regex::Regex;
27
28use crate::{
29    environment::scope::Scope,
30    mel::{MEL_USER_SET_VARIABLE_STRUCT_NAME, interpreter::interpret::BuiltinFunction},
31    prr::Prr,
32};
33
34//BEGIN: Types
35
36#[derive(Debug, Clone, Default)]
37pub struct Struct {
38    pub name: String,
39    fields: HashMap<String, Type>,
40    wild: Vec<(Regex, Type)>,
41}
42
43impl PartialEq for Struct {
44    fn eq(&self, other: &Self) -> bool {
45        self.name == other.name
46    }
47}
48
49impl Struct {
50    pub fn new(name: &str) -> Struct {
51        Struct {
52            name: name.to_string(),
53            fields: HashMap::new(),
54            wild: vec![],
55        }
56    }
57
58    pub fn insert_field(&mut self, name: &str, tipe: Type) {
59        self.fields.insert(name.to_string(), tipe);
60    }
61
62    pub fn insert_wild_field(&mut self, matcher: &Regex, tipe: Type) {
63        self.wild.push((matcher.clone(), tipe));
64    }
65
66    pub fn get_field(&self, name: &str) -> Option<Type> {
67        if let Some(exact_field) = self.fields.get(name) {
68            Some(exact_field.clone())
69        } else {
70            self.wild.iter().find_map(|(regex, tpe)| {
71                if regex.is_match(name) {
72                    Some(tpe.clone())
73                } else {
74                    None
75                }
76            })
77        }
78    }
79
80    pub fn fields(&self) -> impl Iterator<Item = (Option<&String>, Option<&Regex>)> {
81        self.fields
82            .keys()
83            .map(|f| (Some(f), None))
84            .chain(self.wild.iter().map(|f| (None, Some(&f.0))))
85    }
86}
87
88#[derive(Debug, Clone, Default, PartialEq)]
89pub struct ArgumentTypeList {
90    pub args: Vec<Type>,
91}
92
93#[derive(Debug, Clone, Default, PartialEq)]
94pub struct ParameterTypesList {
95    pub params: Vec<Type>,
96}
97
98#[derive(Default, Debug, Clone)]
99pub enum Type {
100    Boolean,
101    Integer,
102    Unsigned,
103    Real,
104    String,
105    Regex,
106    IPAddress,
107    Params(ParameterTypesList),
108    Function(String, ReturnTypeCalculator, ParamsTypeCheckerGenerator),
109    Struct(Struct),
110    #[default]
111    Nil,
112}
113
114impl PartialEq for Type {
115    fn eq(&self, other: &Self) -> bool {
116        match (self, other) {
117            (Self::Params(left), Self::Params(right)) => left == right,
118            (Self::Function(name, _, _), Self::Function(other_name, _, _)) => name == other_name,
119            (Self::Struct(left), Self::Struct(right)) => left == right,
120            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
121        }
122    }
123}
124
125impl Display for Struct {
126    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127        write!(
128            f,
129            "Name: {}, Fields: {}",
130            self.name,
131            self.fields.keys().cloned().collect::<Vec<_>>().join(","),
132        )
133    }
134}
135
136impl Display for Type {
137    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138        match self {
139            Type::Unsigned => write!(f, "Unsigned"),
140            Type::Boolean => write!(f, "Bool"),
141            Type::Integer => write!(f, "Integer"),
142            Type::Real => write!(f, "Real"),
143            Type::String => write!(f, "String"),
144            Type::Regex => write!(f, "Regex"),
145            Type::IPAddress => write!(f, "IPAddress"),
146            Type::Params(items) => write!(
147                f,
148                "Parameters: {}",
149                items
150                    .params
151                    .iter()
152                    .map(|c| c.to_string())
153                    .collect::<Vec<_>>()
154                    .join(","),
155            ),
156            Type::Function(name, rtg, ptcg) => {
157                let rt = rtg();
158                let ptc = ptcg();
159                write!(f, "Name: {name} Return Type: {rt}, Parameter Types: {ptc}")
160            }
161            Type::Struct(s) => write!(f, "Struct: {s}"),
162            Type::Nil => write!(f, "Nil"),
163        }
164    }
165}
166
167impl Struct {
168    pub fn type_for_field(&self, field_name: &str) -> Option<Type> {
169        self.fields.get(field_name).cloned()
170    }
171}
172
173//BEGIN: Simple MEL function parameter type checker.
174
175pub struct SimpleParamTypeChecker {
176    pub p: ParameterTypesList,
177}
178
179impl ParamsTypeChecker for SimpleParamTypeChecker {
180    fn check(&self, a: ArgumentTypeList) -> Result<(), ParamsTypeCheckerError> {
181        if self.p.params.len() != a.args.len() {
182            return Err(ParamsTypeCheckerError::Miscount(
183                self.p.params.len(),
184                a.args.len(),
185            ));
186        }
187
188        for (p, a) in self.p.params.iter().zip(&a.args) {
189            if *p != *a {
190                return Err(ParamsTypeCheckerError::Mismatch(p.clone(), a.clone()));
191            }
192        }
193
194        Ok(())
195    }
196}
197
198impl Display for SimpleParamTypeChecker {
199    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
200        write!(
201            f,
202            "{}",
203            self.p
204                .params
205                .iter()
206                .map(|a| a.to_string())
207                .collect::<Vec<_>>()
208                .join(",")
209        )
210    }
211}
212
213//BEGIN: Simple MEL function parameter type checker for polymorphic functions.
214
215pub struct TypelessParamTypeChecker {
216    pub count: usize,
217}
218
219impl ParamsTypeChecker for TypelessParamTypeChecker {
220    fn check(&self, a: ArgumentTypeList) -> Result<(), ParamsTypeCheckerError> {
221        if self.count != a.args.len() {
222            return Err(ParamsTypeCheckerError::Miscount(self.count, a.args.len()));
223        }
224
225        Ok(())
226    }
227}
228
229impl Display for TypelessParamTypeChecker {
230    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
231        write!(f, "{}", ["Any"].repeat(self.count).join(","))
232    }
233}
234
235//BEGIN: BuiltinFunctionType trait
236
237/// A function that, when called, will return the type of the return type of a MEL function.
238pub type ReturnTypeCalculator = fn() -> Type;
239
240//BEGIN: ParamsTypeChecker trait
241
242#[derive(Debug, Clone)]
243pub enum ParamsTypeCheckerError {
244    Miscount(usize, usize),
245    Mismatch(Type, Type),
246}
247
248#[allow(clippy::result_large_err)]
249pub trait ParamsTypeChecker: Display {
250    fn check(&self, args: ArgumentTypeList) -> Result<(), ParamsTypeCheckerError>;
251}
252
253/// A function that, when called, will return an object that can typecheck function parameters.
254pub type ParamsTypeCheckerGenerator = fn() -> Box<dyn ParamsTypeChecker>;
255
256/// What is required to calculate the type of a MEL function.
257pub trait BuiltinFunctionType: Debug {
258    fn name(&self) -> String;
259    fn params_type_checker(&self) -> ParamsTypeCheckerGenerator;
260    fn return_type_calculator(&self) -> ReturnTypeCalculator;
261}
262
263#[allow(non_camel_case_types)]
264#[derive(Clone, Default, Debug)]
265#[builtin_function(Type::String, Type::String, Type::Integer)]
266pub struct Path_ElementBuiltin {}
267
268#[allow(non_camel_case_types)]
269#[derive(Clone, Default, Debug)]
270#[builtin_function(Type::String, Type::String, Type::Integer, Type::Integer)]
271pub struct Path_ElementsBuiltin {}
272
273#[allow(non_camel_case_types)]
274#[derive(Clone, Default, Debug)]
275#[builtin_function(Type::String, Type::String, Type::String)]
276pub struct MatchBuiltin {}
277
278#[allow(non_camel_case_types)]
279#[derive(Clone, Default, Debug)]
280#[builtin_function(Type::String, Type::String, Type::String, Type::String)]
281pub struct Match_ReplaceBuiltin {}
282
283#[allow(non_camel_case_types)]
284#[derive(Clone, Default, Debug)]
285#[builtin_function(Type::String, Type::String, Type::String, Type::String)]
286pub struct Add_QueryBuiltin {}
287
288#[allow(non_camel_case_types)]
289#[derive(Clone, Default, Debug)]
290#[builtin_function(Type::String, Type::String, Type::String)]
291pub struct Add_Query_MultiBuiltin {}
292
293#[allow(non_camel_case_types)]
294#[derive(Clone, Default, Debug)]
295#[builtin_function(Type::String, Type::String, Type::String)]
296pub struct Remove_QueryBuiltin {}
297
298#[allow(non_camel_case_types)]
299#[derive(Clone, Default, Debug)]
300#[builtin_function(Type::String, Type::String, Type::String)]
301pub struct Remove_Query_MultiBuiltin {}
302
303#[allow(non_camel_case_types)]
304#[derive(Clone, Default, Debug)]
305#[builtin_function(Type::String, Type::String, Type::String)]
306pub struct Keep_Query_MultiBuiltin {}
307
308#[allow(non_camel_case_types)]
309#[derive(Clone, Default, Debug)]
310#[builtin_function(Type::String, Type::String)]
311pub struct LowerBuiltin {}
312
313#[allow(non_camel_case_types)]
314#[derive(Clone, Default, Debug)]
315#[builtin_function(Type::String, Type::String)]
316pub struct UpperBuiltin {}
317
318#[derive(Clone, Default, Debug)]
319pub struct BooleanBuiltin {}
320impl BuiltinFunction for BooleanBuiltin {}
321impl BuiltinFunctionType for BooleanBuiltin {
322    fn name(&self) -> String {
323        "boolean".to_string()
324    }
325
326    fn params_type_checker(&self) -> ParamsTypeCheckerGenerator {
327        || Box::new(TypelessParamTypeChecker { count: 1 })
328    }
329
330    fn return_type_calculator(&self) -> ReturnTypeCalculator {
331        || Type::Boolean
332    }
333}
334
335#[derive(Clone, Default, Debug)]
336pub struct IntegerBuiltin {}
337impl BuiltinFunction for IntegerBuiltin {}
338impl BuiltinFunctionType for IntegerBuiltin {
339    fn name(&self) -> String {
340        "integer".to_string()
341    }
342
343    fn params_type_checker(&self) -> ParamsTypeCheckerGenerator {
344        || Box::new(TypelessParamTypeChecker { count: 1 })
345    }
346
347    fn return_type_calculator(&self) -> ReturnTypeCalculator {
348        || Type::Integer
349    }
350}
351
352#[derive(Clone, Default, Debug)]
353pub struct UnsignedBuiltin {}
354impl BuiltinFunction for UnsignedBuiltin {}
355impl BuiltinFunctionType for UnsignedBuiltin {
356    fn name(&self) -> String {
357        "unsigned".to_string()
358    }
359
360    fn params_type_checker(&self) -> ParamsTypeCheckerGenerator {
361        || Box::new(TypelessParamTypeChecker { count: 1 })
362    }
363
364    fn return_type_calculator(&self) -> ReturnTypeCalculator {
365        || Type::Unsigned
366    }
367}
368
369#[derive(Clone, Default, Debug)]
370pub struct RealBuiltin {}
371impl BuiltinFunction for RealBuiltin {}
372impl BuiltinFunctionType for RealBuiltin {
373    fn name(&self) -> String {
374        "real".to_string()
375    }
376
377    fn params_type_checker(&self) -> ParamsTypeCheckerGenerator {
378        || Box::new(TypelessParamTypeChecker { count: 1 })
379    }
380
381    fn return_type_calculator(&self) -> ReturnTypeCalculator {
382        || Type::Real
383    }
384}
385
386#[derive(Clone, Default, Debug)]
387pub struct StringBuiltin {}
388impl BuiltinFunction for StringBuiltin {}
389impl BuiltinFunctionType for StringBuiltin {
390    fn name(&self) -> String {
391        "string".to_string()
392    }
393
394    fn params_type_checker(&self) -> ParamsTypeCheckerGenerator {
395        || Box::new(TypelessParamTypeChecker { count: 1 })
396    }
397
398    fn return_type_calculator(&self) -> ReturnTypeCalculator {
399        || Type::String
400    }
401}
402
403//BEGIN: Convenience functions for populating [`Scope<Types>`] for [`mel::analysis::analyze`].
404
405pub fn mel_empty_header_type(wild: bool) -> Struct {
406    let mut ht = Struct::new("h");
407    if wild {
408        ht.insert_wild_field(
409            &Regex::new(".*").expect("Could not compile wildcard regex for header type"),
410            Type::String,
411        );
412    }
413
414    ht
415}
416
417pub fn mel_header_type_from_req<A>(value: &http::Request<A>) -> Struct {
418    // Make the header type.
419    let mut ht = Struct::new("h");
420    value.headers().iter().for_each(|header| {
421        ht.insert_field(
422            &header.0.to_string().replace("-", "_").to_lowercase(),
423            Type::String,
424        );
425    });
426    ht
427}
428
429pub fn mel_uri_type() -> Struct {
430    // Make the URI type.
431    let mut urit = Struct::new("uri");
432    urit.insert_field("path", Type::String);
433    urit.insert_field("query", Type::String);
434
435    urit
436}
437
438pub fn mel_req_type(header_type: Struct, uri_type: Struct) -> Struct {
439    // Make the req type.
440    let mut reqs = Struct::new("req");
441    reqs.insert_field("h", Type::Struct(header_type));
442    reqs.insert_field("uri", Type::Struct(uri_type));
443    reqs.insert_field("method", Type::String);
444    reqs.insert_field("scheme", Type::String);
445    reqs.insert_field("clientip", Type::IPAddress);
446    reqs.insert_field("clientport", Type::Integer);
447
448    reqs
449}
450
451pub fn mel_empty_var_type() -> Struct {
452    Struct::new(MEL_USER_SET_VARIABLE_STRUCT_NAME)
453}
454
455pub(crate) fn mel_header_type_from_processable_rr<A>(value: &dyn Prr<A>) -> Struct {
456    // Make the header type.
457    let mut ht = Struct::new("h");
458    value.headers().iter().for_each(|header| {
459        ht.insert_field(
460            &header.0.to_string().replace("-", "_").to_lowercase(),
461            Type::String,
462        );
463    });
464    ht
465}
466
467/// Create a [`Scope<Type>`] that contains the types of the MEL core variables.
468pub fn minimal_core_variable_types() -> Scope<Type> {
469    let mut scope = Scope::<Type>::default();
470    let ht = mel_empty_header_type(true);
471    let vart = mel_empty_var_type();
472    scope = scope.insert("req", Type::Struct(mel_req_type(ht, mel_uri_type())));
473    scope = scope.insert(MEL_USER_SET_VARIABLE_STRUCT_NAME, Type::Struct(vart));
474    scope
475}
476
477macro_rules! add_builtin_function_type_to_scope {
478    ($scope:ident, $builtin:ident) => {
479        $scope.insert(
480            &$builtin.name(),
481            Type::Function(
482                $builtin.name(),
483                $builtin.return_type_calculator(),
484                $builtin.params_type_checker(),
485            ),
486        )
487    };
488}
489
490/// Create a scope that contains the types of the MEL builtin functions.
491pub fn builtin_function_types() -> Scope<Type> {
492    let path_element = Path_ElementBuiltin {};
493    let path_elements = Path_ElementsBuiltin {};
494    let mtch = MatchBuiltin {};
495    let match_replace = Match_ReplaceBuiltin {};
496    let add_query = Add_QueryBuiltin {};
497    let add_query_multi = Add_Query_MultiBuiltin {};
498    let remove_query = Remove_QueryBuiltin {};
499    let remove_query_multi = Remove_Query_MultiBuiltin {};
500    let keep_query_multi = Keep_Query_MultiBuiltin {};
501    let lower = LowerBuiltin {};
502    let upper = UpperBuiltin {};
503
504    // Builtin conversion functions.
505    let boolean = BooleanBuiltin {};
506    let integer = IntegerBuiltin {};
507    let unsigned = UnsignedBuiltin {};
508    let string = StringBuiltin {};
509    let real = RealBuiltin {};
510
511    let mut scopes = Scope::<Type>::default();
512    scopes = add_builtin_function_type_to_scope!(scopes, path_element);
513    scopes = add_builtin_function_type_to_scope!(scopes, path_elements);
514    scopes = add_builtin_function_type_to_scope!(scopes, mtch);
515    scopes = add_builtin_function_type_to_scope!(scopes, match_replace);
516    scopes = add_builtin_function_type_to_scope!(scopes, add_query);
517    scopes = add_builtin_function_type_to_scope!(scopes, add_query_multi);
518    scopes = add_builtin_function_type_to_scope!(scopes, remove_query);
519    scopes = add_builtin_function_type_to_scope!(scopes, remove_query_multi);
520    scopes = add_builtin_function_type_to_scope!(scopes, keep_query_multi);
521    scopes = add_builtin_function_type_to_scope!(scopes, lower);
522    scopes = add_builtin_function_type_to_scope!(scopes, upper);
523
524    scopes = add_builtin_function_type_to_scope!(scopes, boolean);
525    scopes = add_builtin_function_type_to_scope!(scopes, integer);
526    scopes = add_builtin_function_type_to_scope!(scopes, real);
527    scopes = add_builtin_function_type_to_scope!(scopes, string);
528    scopes = add_builtin_function_type_to_scope!(scopes, unsigned);
529    scopes
530}
531
532/// Create a scope that contains the types of only the MEL core variables present in an HTTP request.
533impl<A> From<http::Request<A>> for Scope<Type> {
534    fn from(value: http::Request<A>) -> Self {
535        // Set up the built-in variables for type checking.
536        let mut scope = Scope::<Type>::default();
537
538        let ht = mel_header_type_from_req(&value);
539        let urit = mel_uri_type();
540        let reqs = mel_req_type(ht, urit);
541
542        // Add those types to the scope.
543        scope = scope.insert("req", Type::Struct(reqs));
544
545        scope
546    }
547}
548
549#[cfg(test)]
550mod test_struct {
551    use regex::Regex;
552
553    use crate::mel::types::{Struct, Type};
554
555    #[test]
556    fn test_wild_struct_field() {
557        let mut s = Struct::new("s");
558
559        s.insert_field("testing", Type::String);
560        s.insert_wild_field(
561            &Regex::new("w.*").expect("Could not compile testing regex"),
562            Type::Integer,
563        );
564
565        let found = s
566            .get_field("testing")
567            .expect("Could not find testing field");
568        assert_eq!(found, Type::String);
569
570        let found = s.get_field("wild").expect("Could not find wild field");
571        assert_eq!(found, Type::Integer);
572    }
573}