reqlang_expr/
types.rs

1use std::fmt::{Debug, Display};
2
3use regex::Regex;
4
5use crate::{prelude::BuiltinFn, value::Value};
6
7#[derive(Clone, PartialEq)]
8pub enum Type {
9    Value,
10    String,
11    Fn {
12        args: Vec<Type>,
13        variadic_arg: Option<Box<Type>>,
14        returns: Box<Type>,
15    },
16    Bool,
17    Type(Box<Type>),
18    Unknown,
19}
20
21impl Type {
22    pub fn from(name: &str) -> Self {
23        match name {
24            "String" => Type::String,
25            "Bool" => Type::Bool,
26            "Value" => Type::Value,
27            _ => {
28                if let Some(captures) = Regex::new(r"^Type<(.+)>$").unwrap().captures(name) {
29                    return Type::Type(Type::from(&captures[1]).into());
30                }
31
32                if let Some(captures) = Regex::new(
33                    r"^Fn\((?P<args>(?:\w+(?:,\s*)?)*)\s*(?:\.\.\.(?P<varg>\w+))?\)\s*->\s*(?P<return>\w+)$",
34                ).unwrap().captures(name) {
35                    let args_str = captures.name("args").map_or("", |m| m.as_str());
36                    let variadic_str = captures.name("varg").map_or("", |m| m.as_str());
37                    let return_type_str = captures.name("return").unwrap().as_str();
38
39                    let args: Vec<Type> = if args_str.trim().is_empty() {
40                        vec![]
41                    } else {
42                        args_str.split(',')
43                            .filter_map(|s| {
44                                let trimmed = s.trim();
45                                if trimmed.is_empty() { None } else { Some(Type::from(trimmed)) }
46                            })
47                            .collect()
48                    };
49
50                    let variadic_arg = if variadic_str.is_empty() {
51                        None
52                    } else {
53                        Some(Type::from(variadic_str).into())
54                    };
55
56                    let returns = Type::from(return_type_str).into();
57
58                    return Type::Fn {
59                        args,
60                        variadic_arg,
61                        returns,
62                    };
63                }
64
65                Type::Unknown
66            }
67        }
68    }
69
70    pub fn name(&self) -> String {
71        match self {
72            Type::Value => "Value".to_string(),
73            Type::String => "String".to_string(),
74            Type::Fn {
75                args,
76                variadic_arg,
77                returns,
78            } => {
79                let mut args: Vec<String> = args.iter().map(|arg| arg.name()).collect();
80
81                if let Some(varg) = variadic_arg {
82                    args.push(format!("...{}", varg.name()));
83                }
84
85                let args = args.join(", ");
86
87                let returns = returns.name();
88
89                format!("Fn({args}) -> {returns}")
90            }
91            Type::Bool => "Bool".to_string(),
92            Type::Type(ty) => format!("{}", ty.name()),
93            Type::Unknown => "Unknown".to_string(),
94        }
95    }
96
97    pub fn is_type(&self) -> bool {
98        match self {
99            Type::Type(_) => true,
100            _ => false,
101        }
102    }
103}
104
105impl Display for Type {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        write!(f, "{}", self.name())
108    }
109}
110
111impl Debug for Type {
112    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113        write!(f, "{}", self.name())
114    }
115}
116
117impl From<Value> for Type {
118    fn from(value: Value) -> Self {
119        match value {
120            Value::String(_) => Type::String,
121            Value::Fn(builtin_fn) => {
122                let mut args: Vec<Type> =
123                    builtin_fn.args.iter().map(|arg| arg.ty.clone()).collect();
124
125                let variadic_arg = builtin_fn
126                    .args
127                    .last()
128                    .filter(|arg| arg.variadic)
129                    .map(|arg| Box::new(arg.ty.clone()));
130
131                if variadic_arg.is_some() {
132                    args.pop();
133                }
134
135                Type::Fn {
136                    args,
137                    variadic_arg,
138                    returns: Box::new(builtin_fn.return_type.clone()),
139                }
140            }
141            Value::Bool(_) => Type::Bool,
142            Value::Type(ty) => *ty.clone(),
143        }
144    }
145}
146
147impl From<BuiltinFn<'static>> for Type {
148    fn from(value: BuiltinFn) -> Self {
149        let args: Vec<Type> = value
150            .args
151            .iter()
152            .filter(|x| x.variadic == false)
153            .map(|x| x.ty.clone())
154            .collect();
155        let varg = value
156            .args
157            .iter()
158            .find(|x| x.variadic == true)
159            .map(|x| Box::new(x.ty.clone()));
160        let returns = value.return_type.clone();
161
162        Self::Fn {
163            args,
164            variadic_arg: varg,
165            returns: returns.into(),
166        }
167    }
168}
169
170#[cfg(test)]
171mod from_tests {
172    use crate::prelude::BuiltinFn;
173
174    use super::*;
175
176    #[test]
177    fn test_from_type_value() {
178        let type_value = Value::Type(Type::String.into());
179        let ty: Type = type_value.into();
180        assert_eq!(Type::String, ty);
181    }
182
183    #[test]
184    fn test_get_type_type_value() {
185        let string_value = Value::Type(Type::String.into());
186        let ty: Type = string_value.get_type();
187        assert_eq!(Type::String, ty);
188    }
189
190    #[test]
191    fn test_from_string_value() {
192        let string_value = Value::String("test".to_string());
193        let ty: Type = string_value.into();
194        assert_eq!(Type::String, ty);
195    }
196
197    #[test]
198    fn test_get_type_string_value() {
199        let string_value = Value::String("test".to_string());
200        let ty: Type = string_value.get_type();
201        assert_eq!(Type::String, ty);
202    }
203
204    #[test]
205    fn test_from_bool_value() {
206        let bool_value = Value::Bool(true);
207        let ty: Type = bool_value.into();
208        assert_eq!(Type::Bool, ty);
209    }
210
211    #[test]
212    fn test_get_type_bool_value() {
213        let bool_value = Value::Bool(true);
214        let ty: Type = bool_value.get_type();
215        assert_eq!(Type::Bool, ty);
216    }
217
218    #[test]
219    fn test_from_fn_value() {
220        let builtin_fn = Value::Fn(BuiltinFn::ID.into());
221
222        let ty: Type = builtin_fn.into();
223
224        assert_eq!(
225            Type::Fn {
226                args: vec![Type::Value],
227                variadic_arg: None,
228                returns: Box::new(Type::Value),
229            },
230            ty
231        );
232    }
233
234    #[test]
235    fn test_get_type_fn_value() {
236        let builtin_fn = Value::Fn(BuiltinFn::ID.into());
237
238        let ty: Type = builtin_fn.get_type();
239
240        assert_eq!(
241            Type::Fn {
242                args: vec![Type::Value],
243                variadic_arg: None,
244                returns: Box::new(Type::Value),
245            },
246            ty
247        );
248    }
249}
250
251#[cfg(test)]
252mod name_and_display_tests {
253    use super::*;
254
255    #[test]
256    fn test_name_value() {
257        assert_eq!("Value", Type::Value.name());
258    }
259
260    #[test]
261    fn test_name_string() {
262        assert_eq!("String", Type::String.name());
263    }
264
265    #[test]
266    fn test_name_fn_0_args() {
267        assert_eq!(
268            "Fn() -> String",
269            Type::Fn {
270                args: vec![],
271                variadic_arg: None,
272                returns: Type::String.into(),
273            }
274            .name()
275        );
276    }
277
278    #[test]
279    fn test_name_fn_0_args_and_varadic() {
280        assert_eq!(
281            "Fn(...Value) -> String",
282            Type::Fn {
283                args: vec![],
284                variadic_arg: Some(Type::Value.into()),
285                returns: Type::String.into(),
286            }
287            .name()
288        );
289    }
290
291    #[test]
292    fn test_name_fn_1_args() {
293        assert_eq!(
294            "Fn(Value) -> String",
295            Type::Fn {
296                args: vec![Type::Value],
297                variadic_arg: None,
298                returns: Type::String.into(),
299            }
300            .name()
301        );
302    }
303
304    #[test]
305    fn test_name_fn_2_args() {
306        assert_eq!(
307            "Fn(Value, String) -> String",
308            Type::Fn {
309                args: vec![Type::Value, Type::String],
310                variadic_arg: None,
311                returns: Type::String.into(),
312            }
313            .name()
314        );
315    }
316
317    #[test]
318    fn test_name_fn_2_args_and_varadic() {
319        assert_eq!(
320            "Fn(Value, String, ...Value) -> String",
321            Type::Fn {
322                args: vec![Type::Value, Type::String],
323                variadic_arg: Some(Type::Value.into()),
324                returns: Type::String.into(),
325            }
326            .name()
327        );
328    }
329
330    #[test]
331    fn test_name_bool() {
332        assert_eq!("Bool", Type::Bool.name());
333    }
334}
335
336#[cfg(test)]
337mod from_string_tests {
338    use super::*;
339
340    use pretty_assertions::assert_eq;
341
342    #[test]
343    fn from_string_to_string() {
344        let ty = Type::from("String");
345
346        assert_eq!(Type::String, ty);
347    }
348
349    #[test]
350    fn from_string_to_bool() {
351        let ty = Type::from("Bool");
352
353        assert_eq!(Type::Bool, ty);
354    }
355
356    #[test]
357    fn from_string_to_value() {
358        let ty = Type::from("Value");
359
360        assert_eq!(Type::Value, ty);
361    }
362
363    #[test]
364    fn from_string_to_unknown() {
365        let ty = Type::from("Unknown");
366
367        assert_eq!(Type::Unknown, ty);
368    }
369
370    #[test]
371    fn from_string_to_type_string() {
372        let ty = Type::from("Type<String>");
373
374        assert_eq!(Type::Type(Type::String.into()), ty);
375    }
376
377    #[test]
378    fn from_string_to_type_fn_zero_args() {
379        let ty = Type::from("Fn() -> Value");
380
381        assert_eq!(
382            Type::Fn {
383                args: vec![],
384                variadic_arg: None,
385                returns: Type::Value.into()
386            },
387            ty
388        );
389    }
390
391    #[test]
392    fn from_string_to_type_fn_single_arg() {
393        let ty = Type::from("Fn(String) -> Value");
394
395        assert_eq!(
396            Type::Fn {
397                args: vec![Type::String],
398                variadic_arg: None,
399                returns: Type::Value.into()
400            },
401            ty
402        );
403    }
404
405    #[test]
406    fn from_string_to_type_fn_single_varg() {
407        let ty = Type::from("Fn(...String) -> Value");
408
409        assert_eq!(
410            Type::Fn {
411                args: vec![],
412                variadic_arg: Some(Type::String.into()),
413                returns: Type::Value.into()
414            },
415            ty
416        );
417    }
418
419    #[test]
420    fn from_string_to_type_fn_multiple_args() {
421        let ty = Type::from("Fn(String, Bool, String) -> String");
422
423        assert_eq!(
424            Type::Fn {
425                args: vec![Type::String, Type::Bool, Type::String],
426                variadic_arg: None,
427                returns: Type::String.into()
428            },
429            ty
430        );
431    }
432
433    #[test]
434    fn from_string_to_type_fn_multiple_args_and_varg() {
435        let ty = Type::from("Fn(String, Bool, String, ...Value) -> String");
436
437        assert_eq!(
438            Type::Fn {
439                args: vec![Type::String, Type::Bool, Type::String],
440                variadic_arg: Some(Type::Value.into()),
441                returns: Type::String.into()
442            },
443            ty
444        );
445    }
446}