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
116impl Display for FnArity {
117    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118        let string = match self {
119            FnArity::N(n) => n.to_string(),
120            FnArity::Variadic { n } => {
121                if *n == 0 {
122                    "...".to_string()
123                } else {
124                    format!("{n}, ...")
125                }
126            }
127        };
128
129        write!(f, "{}", string)
130    }
131}
132
133pub struct BuiltinFns;
134
135impl BuiltinFns {
136    pub fn id(args: Vec<Value>) -> Value {
137        let arg = args.first().unwrap();
138
139        arg.clone()
140    }
141
142    pub fn noop(_: Vec<Value>) -> Value {
143        Value::String(String::from("noop"))
144    }
145
146    pub fn is_empty(args: Vec<Value>) -> Value {
147        let string_arg = args
148            .first()
149            .expect("should have string expression passed")
150            .get_string();
151
152        Value::Bool(string_arg.is_empty())
153    }
154
155    pub fn not(args: Vec<Value>) -> Value {
156        let bool_arg = args
157            .first()
158            .expect("should have boolean expression passed")
159            .get_bool();
160
161        Value::Bool(!bool_arg)
162    }
163
164    pub fn and(args: Vec<Value>) -> Value {
165        let a_arg = args
166            .first()
167            .expect("should have first expression passed")
168            .get_bool();
169        let b_arg = args
170            .get(1)
171            .expect("should have second expression passed")
172            .get_bool();
173
174        Value::Bool(a_arg && b_arg)
175    }
176
177    pub fn or(args: Vec<Value>) -> Value {
178        let a_arg = args
179            .first()
180            .expect("should have first expression passed")
181            .get_bool();
182        let b_arg = args
183            .get(1)
184            .expect("should have second expression passed")
185            .get_bool();
186
187        Value::Bool(a_arg || b_arg)
188    }
189
190    pub fn cond(args: Vec<Value>) -> Value {
191        let cond_arg = args
192            .first()
193            .expect("should have cond expression passed")
194            .get_bool();
195        let then_arg = args
196            .get(1)
197            .cloned()
198            .expect("should have then expression passed");
199        let else_arg = args
200            .get(2)
201            .cloned()
202            .expect("should have else expression passed");
203
204        if cond_arg { then_arg } else { else_arg }
205    }
206
207    pub fn to_str(args: Vec<Value>) -> Value {
208        let value_arg = args.first().expect("should have string expression passed");
209
210        match value_arg {
211            Value::String(_) => value_arg.clone(),
212            _ => Value::String(value_arg.to_string()),
213        }
214    }
215
216    pub fn concat(args: Vec<Value>) -> Value {
217        let mut result = String::new();
218
219        for arg in args {
220            let value = match arg {
221                Value::String(string) => string,
222                _ => arg.to_string(),
223            };
224
225            result.push_str(value.as_str());
226        }
227
228        Value::String(result)
229    }
230
231    pub fn contains(args: Vec<Value>) -> Value {
232        let needle_arg = args
233            .first()
234            .expect("should have first expression passed")
235            .get_string();
236        let haystack_arg = args
237            .get(1)
238            .expect("should have second expression passed")
239            .get_string();
240
241        Value::Bool(haystack_arg.contains(needle_arg))
242    }
243
244    pub fn trim(args: Vec<Value>) -> Value {
245        let string_arg = args
246            .first()
247            .expect("should have string expression passed")
248            .get_string();
249
250        Value::String(string_arg.trim().to_string())
251    }
252
253    pub fn trim_start(args: Vec<Value>) -> Value {
254        let string_arg = args
255            .first()
256            .expect("should have string expression passed")
257            .get_string();
258
259        Value::String(string_arg.trim_start().to_string())
260    }
261
262    pub fn trim_end(args: Vec<Value>) -> Value {
263        let string_arg = args
264            .first()
265            .expect("should have string expression passed")
266            .get_string();
267
268        Value::String(string_arg.trim_end().to_string())
269    }
270
271    pub fn lowercase(args: Vec<Value>) -> Value {
272        let string_arg = args
273            .first()
274            .expect("should have string expression passed")
275            .get_string();
276
277        Value::String(string_arg.to_lowercase().to_string())
278    }
279
280    pub fn uppercase(args: Vec<Value>) -> Value {
281        let string_arg = args
282            .first()
283            .expect("should have string expression passed")
284            .get_string();
285
286        Value::String(string_arg.to_uppercase().to_string())
287    }
288
289    pub fn eq(args: Vec<Value>) -> Value {
290        let a_arg = args.first().expect("should have first expression passed");
291        let b_arg = args.get(1).expect("should have second expression passed");
292
293        Value::Bool(a_arg == b_arg)
294    }
295
296    pub fn get_type(args: Vec<Value>) -> Value {
297        let value_arg = args.first().expect("should have first expression passed");
298
299        Value::String(value_arg.get_type().name())
300    }
301}
302
303#[cfg(test)]
304mod value_tests {
305    use std::rc::Rc;
306
307    use super::*;
308
309    #[test]
310    fn test_builtins_display_0_arity() {
311        assert_eq!(
312            "test_builtin() -> String",
313            format!(
314                "{}",
315                BuiltinFn {
316                    name: "test_builtin".to_string(),
317                    args: vec![],
318                    return_type: Type::String,
319                    func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
320                }
321            )
322        )
323    }
324
325    #[test]
326    fn test_builtins_debug_0_arity() {
327        assert_eq!(
328            "test_builtin() -> String",
329            format!(
330                "{:#?}",
331                BuiltinFn {
332                    name: "test_builtin".to_string(),
333                    args: vec![],
334                    return_type: Type::String,
335                    func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
336                }
337            )
338        )
339    }
340
341    #[test]
342    fn test_builtins_display_1_arity() {
343        assert_eq!(
344            "test_builtin(value: String) -> String",
345            format!(
346                "{}",
347                BuiltinFn {
348                    name: "test_builtin".to_string(),
349                    args: vec![FnArg::new("value", Type::String)],
350                    return_type: Type::String,
351                    func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
352                }
353            )
354        )
355    }
356
357    #[test]
358    fn test_builtins_debug_1_arity() {
359        assert_eq!(
360            "test_builtin(value: String) -> String",
361            format!(
362                "{:#?}",
363                BuiltinFn {
364                    name: "test_builtin".to_string(),
365                    args: vec![FnArg::new("value", Type::String)],
366                    return_type: Type::String,
367                    func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
368                }
369            )
370        )
371    }
372
373    #[test]
374    fn test_builtins_display_2_arity() {
375        assert_eq!(
376            "test_builtin(a: String, b: String) -> String",
377            format!(
378                "{}",
379                BuiltinFn {
380                    name: "test_builtin".to_string(),
381                    args: vec![FnArg::new("a", Type::String), FnArg::new("b", Type::String)],
382                    return_type: Type::String,
383                    func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
384                }
385            )
386        )
387    }
388
389    #[test]
390    fn test_builtins_debug_2_arity() {
391        assert_eq!(
392            "test_builtin(a: String, b: String) -> String",
393            format!(
394                "{:#?}",
395                BuiltinFn {
396                    name: "test_builtin".to_string(),
397                    args: vec![FnArg::new("a", Type::String), FnArg::new("b", Type::String)],
398                    return_type: Type::String,
399                    func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
400                }
401            )
402        )
403    }
404}