1use core::fmt;
2use std::fmt::Display;
3
4use crate::{errors::ExprResult, types::Type, value::Value};
5
6#[derive(Clone)]
7pub struct FnArg {
8 pub name: &'static str,
9 pub ty: Type,
10 pub variadic: bool,
11}
12
13impl FnArg {
14 pub fn new(name: &'static str, ty: Type) -> Self {
15 Self {
16 name,
17 ty,
18 variadic: false,
19 }
20 }
21
22 pub fn new_varadic(name: &'static str, ty: Type) -> Self {
23 Self {
24 name,
25 ty,
26 variadic: true,
27 }
28 }
29}
30
31#[derive(Clone)]
32pub struct BuiltinFn<'a> {
34 pub name: &'static str,
36 pub args: &'a [FnArg],
38 pub return_type: Type,
40 pub func: fn(Vec<Value>) -> ExprResult<Value>,
42}
43
44impl<'a> BuiltinFn<'a> {
45 pub fn arity(&self) -> u8 {
46 let len = self.args.len() as u8;
47
48 if self.is_variadic() { len - 1 } else { len }
49 }
50
51 pub fn is_variadic(&self) -> bool {
52 self.args.last().map(|arg| arg.variadic).unwrap_or(false)
53 }
54
55 pub fn arity_matches(&self, arity: u8) -> bool {
56 if self.is_variadic() {
57 self.arity() <= arity
58 } else {
59 self.arity() == arity
60 }
61 }
62
63 pub const DEFAULT_BUILTINS: [BuiltinFn<'a>; 17] = [
67 BuiltinFn::ID,
68 BuiltinFn::NOOP,
69 BuiltinFn::IS_EMPTY,
70 BuiltinFn::AND,
71 BuiltinFn::OR,
72 BuiltinFn::COND,
73 BuiltinFn::TO_STR,
74 BuiltinFn::CONCAT,
75 BuiltinFn::CONTAINS,
76 BuiltinFn::TRIM,
77 BuiltinFn::TRIM_START,
78 BuiltinFn::TRIM_END,
79 BuiltinFn::LOWERCASE,
80 BuiltinFn::UPPERCASE,
81 BuiltinFn::TYPE,
82 BuiltinFn::EQ,
83 BuiltinFn::NOT,
84 ];
85
86 pub const ID: BuiltinFn<'static> = BuiltinFn {
92 name: "id",
93 args: &[FnArg {
94 name: "value",
95 ty: Type::Value,
96 variadic: false,
97 }],
98 return_type: Type::Value,
99 func: Self::id,
100 };
101
102 fn id(args: Vec<Value>) -> ExprResult<Value> {
103 let arg = args.first().unwrap();
104
105 Ok(arg.clone())
106 }
107
108 pub const NOOP: BuiltinFn<'static> = BuiltinFn {
112 name: "noop",
113 args: &[],
114 return_type: Type::String,
115 func: Self::noop,
116 };
117
118 fn noop(_: Vec<Value>) -> ExprResult<Value> {
119 Ok(Value::String(String::from("noop")))
120 }
121
122 pub const IS_EMPTY: BuiltinFn<'static> = BuiltinFn {
126 name: "is_empty",
127 args: &[FnArg {
128 name: "value",
129 ty: Type::String,
130 variadic: false,
131 }],
132 return_type: Type::String,
133 func: Self::is_empty,
134 };
135
136 fn is_empty(args: Vec<Value>) -> ExprResult<Value> {
137 let string_arg = args
138 .first()
139 .expect("should have string expression passed")
140 .get_string()?;
141
142 Ok(Value::Bool(string_arg.is_empty()))
143 }
144
145 pub const AND: BuiltinFn<'static> = BuiltinFn {
149 name: "and",
150 args: &[
151 FnArg {
152 name: "a",
153 ty: Type::Bool,
154 variadic: false,
155 },
156 FnArg {
157 name: "b",
158 ty: Type::Bool,
159 variadic: false,
160 },
161 ],
162 return_type: Type::Bool,
163 func: Self::and,
164 };
165
166 fn and(args: Vec<Value>) -> ExprResult<Value> {
167 let a_arg = args
168 .first()
169 .expect("should have first expression passed")
170 .get_bool()?;
171 let b_arg = args
172 .get(1)
173 .expect("should have second expression passed")
174 .get_bool()?;
175
176 Ok(Value::Bool(a_arg && b_arg))
177 }
178
179 pub const OR: BuiltinFn<'static> = BuiltinFn {
183 name: "or",
184 args: &[
185 FnArg {
186 name: "a",
187 ty: Type::Bool,
188 variadic: false,
189 },
190 FnArg {
191 name: "b",
192 ty: Type::Bool,
193 variadic: false,
194 },
195 ],
196 return_type: Type::Bool,
197 func: Self::or,
198 };
199
200 fn or(args: Vec<Value>) -> ExprResult<Value> {
201 let a_arg = args
202 .first()
203 .expect("should have first expression passed")
204 .get_bool()?;
205 let b_arg = args
206 .get(1)
207 .expect("should have second expression passed")
208 .get_bool()?;
209
210 Ok(Value::Bool(a_arg || b_arg))
211 }
212
213 pub const COND: BuiltinFn<'static> = BuiltinFn {
217 name: "cond",
218 args: &[
219 FnArg {
220 name: "cond",
221 ty: Type::Bool,
222 variadic: false,
223 },
224 FnArg {
225 name: "then",
226 ty: Type::Value,
227 variadic: false,
228 },
229 FnArg {
230 name: "else",
231 ty: Type::Value,
232 variadic: false,
233 },
234 ],
235 return_type: Type::Bool,
236 func: Self::cond,
237 };
238
239 fn cond(args: Vec<Value>) -> ExprResult<Value> {
240 let cond_arg = args
241 .first()
242 .expect("should have cond expression passed")
243 .get_bool()?;
244 let then_arg = args
245 .get(1)
246 .cloned()
247 .expect("should have then expression passed");
248 let else_arg = args
249 .get(2)
250 .cloned()
251 .expect("should have else expression passed");
252
253 if cond_arg { Ok(then_arg) } else { Ok(else_arg) }
254 }
255
256 pub const TO_STR: BuiltinFn<'static> = BuiltinFn {
260 name: "to_str",
261 args: &[FnArg {
262 name: "value",
263 ty: Type::Value,
264 variadic: false,
265 }],
266 return_type: Type::String,
267 func: Self::to_str,
268 };
269
270 fn to_str(args: Vec<Value>) -> ExprResult<Value> {
271 let value_arg = args.first().expect("should have string expression passed");
272
273 Ok(match value_arg {
274 Value::String(_) => value_arg.clone(),
275 _ => Value::String(value_arg.to_string()),
276 })
277 }
278
279 pub const CONCAT: BuiltinFn<'static> = BuiltinFn {
283 name: "concat",
284 args: &[
285 FnArg {
286 name: "a",
287 ty: Type::Value,
288 variadic: false,
289 },
290 FnArg {
291 name: "b",
292 ty: Type::Value,
293 variadic: false,
294 },
295 FnArg {
296 name: "rest",
297 ty: Type::Value,
298 variadic: true,
299 },
300 ],
301 return_type: Type::String,
302 func: Self::concat,
303 };
304
305 fn concat(args: Vec<Value>) -> ExprResult<Value> {
306 let mut result = String::new();
307
308 for arg in args {
309 let value = match arg {
310 Value::String(string) => string,
311 _ => arg.to_string(),
312 };
313
314 result.push_str(value.as_str());
315 }
316
317 Ok(Value::String(result))
318 }
319
320 pub const CONTAINS: BuiltinFn<'static> = BuiltinFn {
324 name: "contains",
325 args: &[
326 FnArg {
327 name: "needle",
328 ty: Type::String,
329 variadic: false,
330 },
331 FnArg {
332 name: "haystack",
333 ty: Type::String,
334 variadic: false,
335 },
336 ],
337 return_type: Type::Bool,
338 func: Self::contains,
339 };
340
341 fn contains(args: Vec<Value>) -> ExprResult<Value> {
342 let needle_arg = args
343 .first()
344 .expect("should have first expression passed")
345 .get_string()?;
346 let haystack_arg = args
347 .get(1)
348 .expect("should have second expression passed")
349 .get_string()?;
350
351 Ok(Value::Bool(haystack_arg.contains(needle_arg)))
352 }
353
354 pub const TRIM: BuiltinFn<'static> = BuiltinFn {
358 name: "trim",
359 args: &[FnArg {
360 name: "value",
361 ty: Type::String,
362 variadic: false,
363 }],
364 return_type: Type::String,
365 func: Self::trim,
366 };
367
368 fn trim(args: Vec<Value>) -> ExprResult<Value> {
369 let string_arg = args
370 .first()
371 .expect("should have string expression passed")
372 .get_string()?;
373
374 Ok(Value::String(string_arg.trim().to_string()))
375 }
376
377 pub const TRIM_START: BuiltinFn<'static> = BuiltinFn {
381 name: "trim_start",
382 args: &[FnArg {
383 name: "value",
384 ty: Type::String,
385 variadic: false,
386 }],
387 return_type: Type::String,
388 func: Self::trim_start,
389 };
390
391 fn trim_start(args: Vec<Value>) -> ExprResult<Value> {
392 let string_arg = args
393 .first()
394 .expect("should have string expression passed")
395 .get_string()?;
396
397 Ok(Value::String(string_arg.trim_start().to_string()))
398 }
399
400 pub const TRIM_END: BuiltinFn<'static> = BuiltinFn {
404 name: "trim_end",
405 args: &[FnArg {
406 name: "value",
407 ty: Type::String,
408 variadic: false,
409 }],
410 return_type: Type::String,
411 func: Self::trim_end,
412 };
413
414 fn trim_end(args: Vec<Value>) -> ExprResult<Value> {
415 let string_arg = args
416 .first()
417 .expect("should have string expression passed")
418 .get_string()?;
419
420 Ok(Value::String(string_arg.trim_end().to_string()))
421 }
422
423 pub const LOWERCASE: BuiltinFn<'static> = BuiltinFn {
427 name: "lowercase",
428 args: &[{
429 FnArg {
430 name: "value",
431 ty: Type::String,
432 variadic: false,
433 }
434 }],
435 return_type: Type::String,
436 func: Self::lowercase,
437 };
438
439 fn lowercase(args: Vec<Value>) -> ExprResult<Value> {
440 let string_arg = args
441 .first()
442 .expect("should have string expression passed")
443 .get_string()?;
444
445 Ok(Value::String(string_arg.to_lowercase().to_string()))
446 }
447
448 pub const UPPERCASE: BuiltinFn<'static> = BuiltinFn {
452 name: "uppercase",
453 args: &[FnArg {
454 name: "value",
455 ty: Type::String,
456 variadic: false,
457 }],
458 return_type: Type::String,
459 func: Self::uppercase,
460 };
461
462 fn uppercase(args: Vec<Value>) -> ExprResult<Value> {
463 let string_arg = args
464 .first()
465 .expect("should have string expression passed")
466 .get_string()?;
467
468 Ok(Value::String(string_arg.to_uppercase().to_string()))
469 }
470
471 pub const TYPE: BuiltinFn<'static> = BuiltinFn {
475 name: "type",
476 args: &[FnArg {
477 name: "value",
478 ty: Type::Value,
479 variadic: false,
480 }],
481 return_type: Type::String,
482 func: Self::get_type,
483 };
484
485 fn get_type(args: Vec<Value>) -> ExprResult<Value> {
486 let value_arg = args.first().expect("should have first expression passed");
487
488 Ok(Value::Type(Type::Type(value_arg.get_type().into()).into()))
489 }
490
491 pub const EQ: BuiltinFn<'static> = BuiltinFn {
495 name: "eq",
496 args: &[
497 {
498 FnArg {
499 name: "a",
500 ty: Type::Value,
501 variadic: false,
502 }
503 },
504 {
505 FnArg {
506 name: "b",
507 ty: Type::Value,
508 variadic: false,
509 }
510 },
511 ],
512 return_type: Type::Bool,
513 func: Self::eq,
514 };
515
516 fn eq(args: Vec<Value>) -> ExprResult<Value> {
517 let first_arg = args.first().expect("should have first expression passed");
518 let second_arg = args.get(1).expect("should have second expression passed");
519
520 let equals = first_arg == second_arg;
521
522 Ok(equals.into())
523 }
524
525 pub const NOT: BuiltinFn<'static> = BuiltinFn {
529 name: "not",
530 args: &[{
531 let ty = Type::Bool;
532 FnArg {
533 name: "value",
534 ty,
535 variadic: false,
536 }
537 }],
538 return_type: Type::Bool,
539 func: Self::not,
540 };
541
542 fn not(args: Vec<Value>) -> ExprResult<Value> {
543 let value_arg = args.first().expect("should have first expression passed");
544
545 let value = &value_arg.get_bool()?;
546
547 Ok(Value::Bool(!value))
548 }
549}
550
551impl<'a> PartialEq for BuiltinFn<'a> {
552 fn eq(&self, other: &Self) -> bool {
553 self.name == other.name
554 }
555}
556
557impl<'a> Display for BuiltinFn<'a> {
558 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
559 let name = &self.name;
560 let args: Vec<String> = self
561 .args
562 .iter()
563 .map(|arg| {
564 let prefix: &str = if arg.variadic { "..." } else { "" };
565
566 format!("{prefix}{}: {}", arg.name, arg.ty.name())
567 })
568 .collect();
569
570 let args: String = args.join(", ");
571
572 let return_type: String = self.return_type.name().to_string();
573
574 write!(f, "{name}({args}) -> {return_type}")
575 }
576}
577
578impl<'a> fmt::Debug for BuiltinFn<'a> {
579 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
580 let name = &self.name;
581 let args: Vec<String> = self
582 .args
583 .iter()
584 .map(|arg| {
585 let prefix: &str = if arg.variadic { "..." } else { "" };
586
587 format!("{prefix}{}: {}", arg.name, arg.ty.name())
588 })
589 .collect();
590
591 let args: String = args.join(", ");
592
593 let return_type: String = self.return_type.name().to_string();
594
595 write!(f, "{name}({args}) -> {return_type}")
596 }
597}
598
599#[derive(Debug, PartialEq)]
600pub enum FnArity {
601 N(u8),
602 Variadic { n: u8 },
603}
604
605#[cfg(test)]
606mod value_tests {
607 use super::*;
608
609 fn example_builtin(_args: Vec<Value>) -> ExprResult<Value> {
610 Ok(Value::String("".to_string()))
611 }
612
613 #[test]
614 fn test_builtins_display_var_arity() {
615 let f = BuiltinFn {
616 name: "test_builtin",
617 args: &[FnArg::new_varadic("rest", Type::String)],
618 return_type: Type::String,
619 func: example_builtin,
620 };
621 assert_eq!("test_builtin(...rest: String) -> String", format!("{}", f))
622 }
623
624 #[test]
625 fn test_builtins_display_0_arity() {
626 assert_eq!(
627 "test_builtin() -> String",
628 format!(
629 "{}",
630 BuiltinFn {
631 name: "test_builtin",
632 args: &[],
633 return_type: Type::String,
634 func: example_builtin
635 }
636 )
637 )
638 }
639
640 #[test]
641 fn test_builtins_debug_0_arity() {
642 assert_eq!(
643 "test_builtin() -> String",
644 format!(
645 "{:#?}",
646 BuiltinFn {
647 name: "test_builtin",
648 args: &[],
649 return_type: Type::String,
650 func: example_builtin
651 }
652 )
653 )
654 }
655
656 #[test]
657 fn test_builtins_display_1_arity() {
658 assert_eq!(
659 "test_builtin(value: String) -> String",
660 format!(
661 "{}",
662 BuiltinFn {
663 name: "test_builtin",
664 args: &[FnArg::new("value", Type::String)],
665 return_type: Type::String,
666 func: example_builtin
667 }
668 )
669 )
670 }
671
672 #[test]
673 fn test_builtins_debug_1_arity() {
674 assert_eq!(
675 "test_builtin(value: String) -> String",
676 format!(
677 "{:#?}",
678 BuiltinFn {
679 name: "test_builtin",
680 args: &[FnArg::new("value", Type::String)],
681 return_type: Type::String,
682 func: example_builtin
683 }
684 )
685 )
686 }
687
688 #[test]
689 fn test_builtins_display_2_arity() {
690 assert_eq!(
691 "test_builtin(a: String, b: String) -> String",
692 format!(
693 "{}",
694 BuiltinFn {
695 name: "test_builtin",
696 args: &[FnArg::new("a", Type::String), FnArg::new("b", Type::String)],
697 return_type: Type::String,
698 func: example_builtin
699 }
700 )
701 )
702 }
703
704 #[test]
705 fn test_builtins_debug_2_arity() {
706 assert_eq!(
707 "test_builtin(a: String, b: String) -> String",
708 format!(
709 "{:#?}",
710 BuiltinFn {
711 name: "test_builtin",
712 args: &[FnArg::new("a", Type::String), FnArg::new("b", Type::String)],
713 return_type: Type::String,
714 func: example_builtin
715 }
716 )
717 )
718 }
719}