reqlang_expr/
types.rs

1use std::fmt::{Debug, Display};
2
3use crate::value::Value;
4
5#[derive(Clone, PartialEq)]
6pub enum Type {
7    Value,
8    String,
9    Fn {
10        args: Vec<Type>,
11        variadic_arg: Option<Box<Type>>,
12        returns: Box<Type>,
13    },
14    Bool,
15}
16
17impl Type {
18    pub fn name(&self) -> String {
19        match self {
20            Type::Value => "Value".to_string(),
21            Type::String => "String".to_string(),
22            Type::Fn {
23                args,
24                variadic_arg,
25                returns,
26            } => {
27                let mut args: Vec<String> = args.iter().map(|arg| arg.name()).collect();
28
29                if let Some(varg) = variadic_arg {
30                    args.push(format!("...{}", varg.name()));
31                }
32
33                let args = args.join(", ");
34
35                let returns = returns.name();
36
37                format!("Fn({args}) -> {returns}")
38            }
39            Type::Bool => "Bool".to_string(),
40        }
41    }
42}
43
44impl Display for Type {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        write!(f, "{}", self.name())
47    }
48}
49
50impl Debug for Type {
51    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52        write!(f, "{}", self.name())
53    }
54}
55
56impl From<Value> for Type {
57    fn from(value: Value) -> Self {
58        match value {
59            Value::String(_) => Type::String,
60            Value::Fn(builtin_fn) => {
61                let mut args: Vec<Type> =
62                    builtin_fn.args.iter().map(|arg| arg.ty.clone()).collect();
63
64                let variadic_arg = builtin_fn
65                    .args
66                    .last()
67                    .filter(|arg| arg.variadic)
68                    .map(|arg| Box::new(arg.ty.clone()));
69
70                if variadic_arg.is_some() {
71                    args.pop();
72                }
73
74                Type::Fn {
75                    args,
76                    variadic_arg,
77                    returns: Box::new(builtin_fn.return_type.clone()),
78                }
79            }
80            Value::Bool(_) => Type::Bool,
81        }
82    }
83}
84
85#[cfg(test)]
86mod from_tests {
87    use std::rc::Rc;
88
89    use crate::prelude::{BuiltinFn, FnArg};
90
91    use super::*;
92
93    #[test]
94    fn test_from_string_value() {
95        let string_value = Value::String("test".to_string());
96        let ty: Type = string_value.into();
97        assert_eq!(Type::String, ty);
98    }
99
100    #[test]
101    fn test_get_type_string_value() {
102        let string_value = Value::String("test".to_string());
103        let ty: Type = string_value.get_type();
104        assert_eq!(Type::String, ty);
105    }
106
107    #[test]
108    fn test_from_bool_value() {
109        let bool_value = Value::Bool(true);
110        let ty: Type = bool_value.into();
111        assert_eq!(Type::Bool, ty);
112    }
113
114    #[test]
115    fn test_get_type_bool_value() {
116        let bool_value = Value::Bool(true);
117        let ty: Type = bool_value.get_type();
118        assert_eq!(Type::Bool, ty);
119    }
120
121    #[test]
122    fn test_from_fn_value() {
123        let builtin_fn = Value::Fn(Rc::new(BuiltinFn {
124            name: "test".to_string(),
125            args: vec![FnArg::new("a", Type::Value)],
126            return_type: Type::String,
127            func: Rc::new(|_| Value::String("".to_string())),
128        }));
129
130        let ty: Type = builtin_fn.into();
131
132        assert_eq!(
133            Type::Fn {
134                args: vec![Type::Value],
135                variadic_arg: None,
136                returns: Box::new(Type::String),
137            },
138            ty
139        );
140    }
141
142    #[test]
143    fn test_get_type_fn_value() {
144        let builtin_fn = Value::Fn(Rc::new(BuiltinFn {
145            name: "test".to_string(),
146            args: vec![FnArg::new("a", Type::Value)],
147            return_type: Type::String,
148            func: Rc::new(|_| Value::String("".to_string())),
149        }));
150
151        let ty: Type = builtin_fn.get_type();
152
153        assert_eq!(
154            Type::Fn {
155                args: vec![Type::Value],
156                variadic_arg: None,
157                returns: Box::new(Type::String),
158            },
159            ty
160        );
161    }
162}
163
164#[cfg(test)]
165mod name_and_display_tests {
166    use super::*;
167
168    #[test]
169    fn test_name_value() {
170        assert_eq!("Value", Type::Value.name());
171    }
172
173    #[test]
174    fn test_name_string() {
175        assert_eq!("String", Type::String.name());
176    }
177
178    #[test]
179    fn test_name_fn_0_args() {
180        assert_eq!(
181            "Fn() -> String",
182            Type::Fn {
183                args: vec![],
184                variadic_arg: None,
185                returns: Type::String.into(),
186            }
187            .name()
188        );
189    }
190
191    #[test]
192    fn test_name_fn_0_args_and_varadic() {
193        assert_eq!(
194            "Fn(...Value) -> String",
195            Type::Fn {
196                args: vec![],
197                variadic_arg: Some(Type::Value.into()),
198                returns: Type::String.into(),
199            }
200            .name()
201        );
202    }
203
204    #[test]
205    fn test_name_fn_1_args() {
206        assert_eq!(
207            "Fn(Value) -> String",
208            Type::Fn {
209                args: vec![Type::Value],
210                variadic_arg: None,
211                returns: Type::String.into(),
212            }
213            .name()
214        );
215    }
216
217    #[test]
218    fn test_name_fn_2_args() {
219        assert_eq!(
220            "Fn(Value, String) -> String",
221            Type::Fn {
222                args: vec![Type::Value, Type::String],
223                variadic_arg: None,
224                returns: Type::String.into(),
225            }
226            .name()
227        );
228    }
229
230    #[test]
231    fn test_name_fn_2_args_and_varadic() {
232        assert_eq!(
233            "Fn(Value, String, ...Value) -> String",
234            Type::Fn {
235                args: vec![Type::Value, Type::String],
236                variadic_arg: Some(Type::Value.into()),
237                returns: Type::String.into(),
238            }
239            .name()
240        );
241    }
242
243    #[test]
244    fn test_name_bool() {
245        assert_eq!("Bool", Type::Bool.name());
246    }
247}