reqlang_expr/
builtins.rs

1use core::fmt;
2use std::fmt::Display;
3
4use crate::{types::Type, value::Value};
5
6#[derive(Clone)]
7pub struct FnArg {
8    pub name: String,
9    pub ty: Type,
10    pub variadic: bool,
11}
12
13impl FnArg {
14    pub fn new(name: &str, ty: Type) -> Self {
15        Self {
16            name: String::from(name),
17            ty,
18            variadic: false,
19        }
20    }
21
22    pub fn new_varadic(name: &str, ty: Type) -> Self {
23        Self {
24            name: String::from(name),
25            ty,
26            variadic: true,
27        }
28    }
29}
30
31/// Builtin function used in expressions
32pub struct BuiltinFn {
33    // Needs to follow identifier naming rules
34    pub name: String,
35    // Arguments the function expects
36    pub args: Vec<FnArg>,
37    pub return_type: Type,
38    // Function used at runtime
39    pub func: std::rc::Rc<dyn Fn(Vec<Value>) -> Value>,
40}
41
42impl BuiltinFn {
43    pub fn arity(&self) -> u8 {
44        let len = self.args.len() as u8;
45
46        if self.is_variadic() { len - 1 } else { len }
47    }
48
49    pub fn is_variadic(&self) -> bool {
50        self.args.last().map(|arg| arg.variadic).unwrap_or(false)
51    }
52
53    pub fn arity_matches(&self, arity: u8) -> bool {
54        if self.is_variadic() {
55            self.arity() <= arity
56        } else {
57            self.arity() == arity
58        }
59    }
60}
61
62impl PartialEq for BuiltinFn {
63    fn eq(&self, other: &Self) -> bool {
64        self.name == other.name
65    }
66}
67
68impl Display for BuiltinFn {
69    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
70        let name = &self.name;
71        let args: Vec<String> = self
72            .args
73            .iter()
74            .map(|arg| {
75                let prefix: &str = if arg.variadic { "..." } else { "" };
76
77                format!("{prefix}{}: {}", arg.name.clone(), arg.ty.name())
78            })
79            .collect();
80
81        let args: String = args.join(", ");
82
83        let return_type: String = self.return_type.name().to_string();
84
85        write!(f, "{name}({args}) -> {return_type}")
86    }
87}
88
89impl fmt::Debug for BuiltinFn {
90    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91        let name = &self.name;
92        let args: Vec<String> = self
93            .args
94            .iter()
95            .map(|arg| {
96                let prefix: &str = if arg.variadic { "..." } else { "" };
97
98                format!("{prefix}{}: {}", arg.name.clone(), arg.ty.name())
99            })
100            .collect();
101
102        let args: String = args.join(", ");
103
104        let return_type: String = self.return_type.name().to_string();
105
106        write!(f, "{name}({args}) -> {return_type}")
107    }
108}
109
110#[derive(Debug, PartialEq)]
111pub enum FnArity {
112    N(u8),
113    Variadic { n: u8 },
114}
115
116pub struct BuiltinFns;
117
118impl BuiltinFns {
119    pub fn id(args: Vec<Value>) -> Value {
120        let arg = args.first().unwrap();
121
122        arg.clone()
123    }
124
125    pub fn noop(_: Vec<Value>) -> Value {
126        Value::String(String::from("noop"))
127    }
128
129    pub fn is_empty(args: Vec<Value>) -> Value {
130        let string_arg = args
131            .first()
132            .expect("should have string expression passed")
133            .get_string();
134
135        Value::Bool(string_arg.is_empty())
136    }
137
138    pub fn and(args: Vec<Value>) -> Value {
139        let a_arg = args
140            .first()
141            .expect("should have first expression passed")
142            .get_bool();
143        let b_arg = args
144            .get(1)
145            .expect("should have second expression passed")
146            .get_bool();
147
148        Value::Bool(a_arg && b_arg)
149    }
150
151    pub fn or(args: Vec<Value>) -> Value {
152        let a_arg = args
153            .first()
154            .expect("should have first expression passed")
155            .get_bool();
156        let b_arg = args
157            .get(1)
158            .expect("should have second expression passed")
159            .get_bool();
160
161        Value::Bool(a_arg || b_arg)
162    }
163
164    pub fn cond(args: Vec<Value>) -> Value {
165        let cond_arg = args
166            .first()
167            .expect("should have cond expression passed")
168            .get_bool();
169        let then_arg = args
170            .get(1)
171            .cloned()
172            .expect("should have then expression passed");
173        let else_arg = args
174            .get(2)
175            .cloned()
176            .expect("should have else expression passed");
177
178        if cond_arg { then_arg } else { else_arg }
179    }
180
181    pub fn to_str(args: Vec<Value>) -> Value {
182        let value_arg = args.first().expect("should have string expression passed");
183
184        match value_arg {
185            Value::String(_) => value_arg.clone(),
186            _ => Value::String(value_arg.to_string()),
187        }
188    }
189
190    pub fn concat(args: Vec<Value>) -> Value {
191        let mut result = String::new();
192
193        for arg in args {
194            let value = match arg {
195                Value::String(string) => string,
196                _ => arg.to_string(),
197            };
198
199            result.push_str(value.as_str());
200        }
201
202        Value::String(result)
203    }
204
205    pub fn contains(args: Vec<Value>) -> Value {
206        let needle_arg = args
207            .first()
208            .expect("should have first expression passed")
209            .get_string();
210        let haystack_arg = args
211            .get(1)
212            .expect("should have second expression passed")
213            .get_string();
214
215        Value::Bool(haystack_arg.contains(needle_arg))
216    }
217
218    pub fn trim(args: Vec<Value>) -> Value {
219        let string_arg = args
220            .first()
221            .expect("should have string expression passed")
222            .get_string();
223
224        Value::String(string_arg.trim().to_string())
225    }
226
227    pub fn trim_start(args: Vec<Value>) -> Value {
228        let string_arg = args
229            .first()
230            .expect("should have string expression passed")
231            .get_string();
232
233        Value::String(string_arg.trim_start().to_string())
234    }
235
236    pub fn trim_end(args: Vec<Value>) -> Value {
237        let string_arg = args
238            .first()
239            .expect("should have string expression passed")
240            .get_string();
241
242        Value::String(string_arg.trim_end().to_string())
243    }
244
245    pub fn lowercase(args: Vec<Value>) -> Value {
246        let string_arg = args
247            .first()
248            .expect("should have string expression passed")
249            .get_string();
250
251        Value::String(string_arg.to_lowercase().to_string())
252    }
253
254    pub fn uppercase(args: Vec<Value>) -> Value {
255        let string_arg = args
256            .first()
257            .expect("should have string expression passed")
258            .get_string();
259
260        Value::String(string_arg.to_uppercase().to_string())
261    }
262}
263
264#[cfg(test)]
265mod value_tests {
266    use std::rc::Rc;
267
268    use super::*;
269
270    #[test]
271    fn test_builtins_display_var_arity() {
272        assert_eq!(
273            "test_builtin(...rest: String) -> String",
274            format!(
275                "{}",
276                BuiltinFn {
277                    name: "test_builtin".to_string(),
278                    args: vec![FnArg::new_varadic("rest", Type::String)],
279                    return_type: Type::String,
280                    func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
281                }
282            )
283        )
284    }
285
286    #[test]
287    fn test_builtins_display_0_arity() {
288        assert_eq!(
289            "test_builtin() -> String",
290            format!(
291                "{}",
292                BuiltinFn {
293                    name: "test_builtin".to_string(),
294                    args: vec![],
295                    return_type: Type::String,
296                    func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
297                }
298            )
299        )
300    }
301
302    #[test]
303    fn test_builtins_debug_0_arity() {
304        assert_eq!(
305            "test_builtin() -> String",
306            format!(
307                "{:#?}",
308                BuiltinFn {
309                    name: "test_builtin".to_string(),
310                    args: vec![],
311                    return_type: Type::String,
312                    func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
313                }
314            )
315        )
316    }
317
318    #[test]
319    fn test_builtins_display_1_arity() {
320        assert_eq!(
321            "test_builtin(value: String) -> String",
322            format!(
323                "{}",
324                BuiltinFn {
325                    name: "test_builtin".to_string(),
326                    args: vec![FnArg::new("value", Type::String)],
327                    return_type: Type::String,
328                    func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
329                }
330            )
331        )
332    }
333
334    #[test]
335    fn test_builtins_debug_1_arity() {
336        assert_eq!(
337            "test_builtin(value: String) -> String",
338            format!(
339                "{:#?}",
340                BuiltinFn {
341                    name: "test_builtin".to_string(),
342                    args: vec![FnArg::new("value", Type::String)],
343                    return_type: Type::String,
344                    func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
345                }
346            )
347        )
348    }
349
350    #[test]
351    fn test_builtins_display_2_arity() {
352        assert_eq!(
353            "test_builtin(a: String, b: String) -> String",
354            format!(
355                "{}",
356                BuiltinFn {
357                    name: "test_builtin".to_string(),
358                    args: vec![FnArg::new("a", Type::String), FnArg::new("b", Type::String)],
359                    return_type: Type::String,
360                    func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
361                }
362            )
363        )
364    }
365
366    #[test]
367    fn test_builtins_debug_2_arity() {
368        assert_eq!(
369            "test_builtin(a: String, b: String) -> String",
370            format!(
371                "{:#?}",
372                BuiltinFn {
373                    name: "test_builtin".to_string(),
374                    args: vec![FnArg::new("a", Type::String), FnArg::new("b", Type::String)],
375                    return_type: Type::String,
376                    func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
377                }
378            )
379        )
380    }
381}