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>; 19] = [
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 BuiltinFn::BASE64_ENCODE,
85 BuiltinFn::BASE64_DECODE,
86 ];
87
88 pub const ID: BuiltinFn<'static> = BuiltinFn {
94 name: "id",
95 args: &[FnArg {
96 name: "value",
97 ty: Type::Value,
98 variadic: false,
99 }],
100 return_type: Type::Value,
101 func: Self::id,
102 };
103
104 fn id(args: Vec<Value>) -> ExprResult<Value> {
105 let arg = args.first().unwrap();
106
107 Ok(arg.clone())
108 }
109
110 pub const NOOP: BuiltinFn<'static> = BuiltinFn {
114 name: "noop",
115 args: &[],
116 return_type: Type::String,
117 func: Self::noop,
118 };
119
120 fn noop(_: Vec<Value>) -> ExprResult<Value> {
121 Ok(Value::String(String::from("noop")))
122 }
123
124 pub const IS_EMPTY: BuiltinFn<'static> = BuiltinFn {
128 name: "is_empty",
129 args: &[FnArg {
130 name: "value",
131 ty: Type::String,
132 variadic: false,
133 }],
134 return_type: Type::Bool,
135 func: Self::is_empty,
136 };
137
138 fn is_empty(args: Vec<Value>) -> ExprResult<Value> {
139 let string_arg = args
140 .first()
141 .expect("should have string expression passed")
142 .get_string()?;
143
144 Ok(Value::Bool(string_arg.is_empty()))
145 }
146
147 pub const AND: BuiltinFn<'static> = BuiltinFn {
151 name: "and",
152 args: &[
153 FnArg {
154 name: "a",
155 ty: Type::Bool,
156 variadic: false,
157 },
158 FnArg {
159 name: "b",
160 ty: Type::Bool,
161 variadic: false,
162 },
163 ],
164 return_type: Type::Bool,
165 func: Self::and,
166 };
167
168 fn and(args: Vec<Value>) -> ExprResult<Value> {
169 let a_arg = args
170 .first()
171 .expect("should have first expression passed")
172 .get_bool()?;
173 let b_arg = args
174 .get(1)
175 .expect("should have second expression passed")
176 .get_bool()?;
177
178 Ok(Value::Bool(a_arg && b_arg))
179 }
180
181 pub const OR: BuiltinFn<'static> = BuiltinFn {
185 name: "or",
186 args: &[
187 FnArg {
188 name: "a",
189 ty: Type::Bool,
190 variadic: false,
191 },
192 FnArg {
193 name: "b",
194 ty: Type::Bool,
195 variadic: false,
196 },
197 ],
198 return_type: Type::Bool,
199 func: Self::or,
200 };
201
202 fn or(args: Vec<Value>) -> ExprResult<Value> {
203 let a_arg = args
204 .first()
205 .expect("should have first expression passed")
206 .get_bool()?;
207 let b_arg = args
208 .get(1)
209 .expect("should have second expression passed")
210 .get_bool()?;
211
212 Ok(Value::Bool(a_arg || b_arg))
213 }
214
215 pub const COND: BuiltinFn<'static> = BuiltinFn {
219 name: "cond",
220 args: &[
221 FnArg {
222 name: "cond",
223 ty: Type::Bool,
224 variadic: false,
225 },
226 FnArg {
227 name: "then",
228 ty: Type::Value,
229 variadic: false,
230 },
231 FnArg {
232 name: "else",
233 ty: Type::Value,
234 variadic: false,
235 },
236 ],
237 return_type: Type::Bool,
238 func: Self::cond,
239 };
240
241 fn cond(args: Vec<Value>) -> ExprResult<Value> {
242 let cond_arg = args
243 .first()
244 .expect("should have cond expression passed")
245 .get_bool()?;
246 let then_arg = args
247 .get(1)
248 .cloned()
249 .expect("should have then expression passed");
250 let else_arg = args
251 .get(2)
252 .cloned()
253 .expect("should have else expression passed");
254
255 if cond_arg { Ok(then_arg) } else { Ok(else_arg) }
256 }
257
258 pub const TO_STR: BuiltinFn<'static> = BuiltinFn {
262 name: "to_str",
263 args: &[FnArg {
264 name: "value",
265 ty: Type::Value,
266 variadic: false,
267 }],
268 return_type: Type::String,
269 func: Self::to_str,
270 };
271
272 fn to_str(args: Vec<Value>) -> ExprResult<Value> {
273 let value_arg = args.first().expect("should have string expression passed");
274
275 Ok(match value_arg {
276 Value::String(_) => value_arg.clone(),
277 _ => Value::String(value_arg.to_string()),
278 })
279 }
280
281 pub const CONCAT: BuiltinFn<'static> = BuiltinFn {
285 name: "concat",
286 args: &[
287 FnArg {
288 name: "a",
289 ty: Type::Value,
290 variadic: false,
291 },
292 FnArg {
293 name: "b",
294 ty: Type::Value,
295 variadic: false,
296 },
297 FnArg {
298 name: "rest",
299 ty: Type::Value,
300 variadic: true,
301 },
302 ],
303 return_type: Type::String,
304 func: Self::concat,
305 };
306
307 fn concat(args: Vec<Value>) -> ExprResult<Value> {
308 let mut result = String::new();
309
310 for arg in args {
311 let value = match arg {
312 Value::String(string) => string,
313 _ => arg.to_string(),
314 };
315
316 result.push_str(value.as_str());
317 }
318
319 Ok(Value::String(result))
320 }
321
322 pub const CONTAINS: BuiltinFn<'static> = BuiltinFn {
326 name: "contains",
327 args: &[
328 FnArg {
329 name: "needle",
330 ty: Type::String,
331 variadic: false,
332 },
333 FnArg {
334 name: "haystack",
335 ty: Type::String,
336 variadic: false,
337 },
338 ],
339 return_type: Type::Bool,
340 func: Self::contains,
341 };
342
343 fn contains(args: Vec<Value>) -> ExprResult<Value> {
344 let needle_arg = args
345 .first()
346 .expect("should have first expression passed")
347 .get_string()?;
348 let haystack_arg = args
349 .get(1)
350 .expect("should have second expression passed")
351 .get_string()?;
352
353 Ok(Value::Bool(haystack_arg.contains(needle_arg)))
354 }
355
356 pub const TRIM: BuiltinFn<'static> = BuiltinFn {
360 name: "trim",
361 args: &[FnArg {
362 name: "value",
363 ty: Type::String,
364 variadic: false,
365 }],
366 return_type: Type::String,
367 func: Self::trim,
368 };
369
370 fn trim(args: Vec<Value>) -> ExprResult<Value> {
371 let string_arg = args
372 .first()
373 .expect("should have string expression passed")
374 .get_string()?;
375
376 Ok(Value::String(string_arg.trim().to_string()))
377 }
378
379 pub const TRIM_START: BuiltinFn<'static> = BuiltinFn {
383 name: "trim_start",
384 args: &[FnArg {
385 name: "value",
386 ty: Type::String,
387 variadic: false,
388 }],
389 return_type: Type::String,
390 func: Self::trim_start,
391 };
392
393 fn trim_start(args: Vec<Value>) -> ExprResult<Value> {
394 let string_arg = args
395 .first()
396 .expect("should have string expression passed")
397 .get_string()?;
398
399 Ok(Value::String(string_arg.trim_start().to_string()))
400 }
401
402 pub const TRIM_END: BuiltinFn<'static> = BuiltinFn {
406 name: "trim_end",
407 args: &[FnArg {
408 name: "value",
409 ty: Type::String,
410 variadic: false,
411 }],
412 return_type: Type::String,
413 func: Self::trim_end,
414 };
415
416 fn trim_end(args: Vec<Value>) -> ExprResult<Value> {
417 let string_arg = args
418 .first()
419 .expect("should have string expression passed")
420 .get_string()?;
421
422 Ok(Value::String(string_arg.trim_end().to_string()))
423 }
424
425 pub const LOWERCASE: BuiltinFn<'static> = BuiltinFn {
429 name: "lowercase",
430 args: &[{
431 FnArg {
432 name: "value",
433 ty: Type::String,
434 variadic: false,
435 }
436 }],
437 return_type: Type::String,
438 func: Self::lowercase,
439 };
440
441 fn lowercase(args: Vec<Value>) -> ExprResult<Value> {
442 let string_arg = args
443 .first()
444 .expect("should have string expression passed")
445 .get_string()?;
446
447 Ok(Value::String(string_arg.to_lowercase().to_string()))
448 }
449
450 pub const UPPERCASE: BuiltinFn<'static> = BuiltinFn {
454 name: "uppercase",
455 args: &[FnArg {
456 name: "value",
457 ty: Type::String,
458 variadic: false,
459 }],
460 return_type: Type::String,
461 func: Self::uppercase,
462 };
463
464 fn uppercase(args: Vec<Value>) -> ExprResult<Value> {
465 let string_arg = args
466 .first()
467 .expect("should have string expression passed")
468 .get_string()?;
469
470 Ok(Value::String(string_arg.to_uppercase().to_string()))
471 }
472
473 pub const TYPE: BuiltinFn<'static> = BuiltinFn {
477 name: "type",
478 args: &[FnArg {
479 name: "value",
480 ty: Type::Value,
481 variadic: false,
482 }],
483 return_type: Type::String,
484 func: Self::get_type,
485 };
486
487 fn get_type(args: Vec<Value>) -> ExprResult<Value> {
488 let value_arg = args.first().expect("should have first expression passed");
489
490 Ok(Value::Type(Type::Type(value_arg.get_type().into()).into()))
491 }
492
493 pub const EQ: BuiltinFn<'static> = BuiltinFn {
497 name: "eq",
498 args: &[
499 {
500 FnArg {
501 name: "a",
502 ty: Type::Value,
503 variadic: false,
504 }
505 },
506 {
507 FnArg {
508 name: "b",
509 ty: Type::Value,
510 variadic: false,
511 }
512 },
513 ],
514 return_type: Type::Bool,
515 func: Self::eq,
516 };
517
518 fn eq(args: Vec<Value>) -> ExprResult<Value> {
519 let first_arg = args.first().expect("should have first expression passed");
520 let second_arg = args.get(1).expect("should have second expression passed");
521
522 let equals = first_arg == second_arg;
523
524 Ok(equals.into())
525 }
526
527 pub const NOT: BuiltinFn<'static> = BuiltinFn {
531 name: "not",
532 args: &[{
533 let ty = Type::Bool;
534 FnArg {
535 name: "value",
536 ty,
537 variadic: false,
538 }
539 }],
540 return_type: Type::Bool,
541 func: Self::not,
542 };
543
544 fn not(args: Vec<Value>) -> ExprResult<Value> {
545 let value_arg = args.first().expect("should have first expression passed");
546
547 let value = &value_arg.get_bool()?;
548
549 Ok(Value::Bool(!value))
550 }
551
552 pub const BASE64_ENCODE: BuiltinFn<'static> = BuiltinFn {
556 name: "b64encode",
557 args: &[FnArg {
558 name: "value",
559 ty: Type::Value,
560 variadic: false,
561 }],
562 return_type: Type::Value,
563 func: Self::base64_encode,
564 };
565
566 fn base64_encode(args: Vec<Value>) -> ExprResult<Value> {
567 let string_arg = args
568 .first()
569 .expect("should have string expression passed")
570 .get_string()?;
571
572 use base64::prelude::*;
573
574 let encoded = BASE64_STANDARD.encode(string_arg);
575
576 Ok(Value::String(encoded))
577 }
578
579 pub const BASE64_DECODE: BuiltinFn<'static> = BuiltinFn {
583 name: "b64decode",
584 args: &[FnArg {
585 name: "value",
586 ty: Type::Value,
587 variadic: false,
588 }],
589 return_type: Type::Value,
590 func: Self::base64_decode,
591 };
592
593 fn base64_decode(args: Vec<Value>) -> ExprResult<Value> {
594 let string_arg = args
595 .first()
596 .expect("should have string expression passed")
597 .get_string()?;
598
599 use base64::prelude::*;
600
601 let decoded = BASE64_STANDARD
602 .decode(string_arg)
603 .expect("unable to decode");
604
605 Ok(Value::String(String::from_utf8(decoded).unwrap()))
606 }
607}
608
609impl<'a> PartialEq for BuiltinFn<'a> {
610 fn eq(&self, other: &Self) -> bool {
611 self.name == other.name
612 }
613}
614
615impl<'a> Display for BuiltinFn<'a> {
616 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
617 let name = &self.name;
618 let args: Vec<String> = self
619 .args
620 .iter()
621 .map(|arg| {
622 let prefix: &str = if arg.variadic { "..." } else { "" };
623
624 format!("{prefix}{}: {}", arg.name, arg.ty.name())
625 })
626 .collect();
627
628 let args: String = args.join(", ");
629
630 let return_type: String = self.return_type.name().to_string();
631
632 write!(f, "{name}({args}) -> {return_type}")
633 }
634}
635
636impl<'a> fmt::Debug for BuiltinFn<'a> {
637 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
638 let name = &self.name;
639 let args: Vec<String> = self
640 .args
641 .iter()
642 .map(|arg| {
643 let prefix: &str = if arg.variadic { "..." } else { "" };
644
645 format!("{prefix}{}: {}", arg.name, arg.ty.name())
646 })
647 .collect();
648
649 let args: String = args.join(", ");
650
651 let return_type: String = self.return_type.name().to_string();
652
653 write!(f, "{name}({args}) -> {return_type}")
654 }
655}
656
657#[derive(Debug, PartialEq)]
658pub enum FnArity {
659 N(u8),
660 Variadic { n: u8 },
661}
662
663#[cfg(test)]
664mod value_tests {
665 use super::*;
666
667 fn example_builtin(_args: Vec<Value>) -> ExprResult<Value> {
668 Ok(Value::String("".to_string()))
669 }
670
671 #[test]
672 fn test_builtins_display_var_arity() {
673 let f = BuiltinFn {
674 name: "test_builtin",
675 args: &[FnArg::new_varadic("rest", Type::String)],
676 return_type: Type::String,
677 func: example_builtin,
678 };
679 assert_eq!("test_builtin(...rest: String) -> String", format!("{f}"))
680 }
681
682 #[test]
683 fn test_builtins_display_0_arity() {
684 assert_eq!(
685 "test_builtin() -> String",
686 format!(
687 "{}",
688 BuiltinFn {
689 name: "test_builtin",
690 args: &[],
691 return_type: Type::String,
692 func: example_builtin
693 }
694 )
695 )
696 }
697
698 #[test]
699 fn test_builtins_debug_0_arity() {
700 assert_eq!(
701 "test_builtin() -> String",
702 format!(
703 "{:#?}",
704 BuiltinFn {
705 name: "test_builtin",
706 args: &[],
707 return_type: Type::String,
708 func: example_builtin
709 }
710 )
711 )
712 }
713
714 #[test]
715 fn test_builtins_display_1_arity() {
716 assert_eq!(
717 "test_builtin(value: String) -> String",
718 format!(
719 "{}",
720 BuiltinFn {
721 name: "test_builtin",
722 args: &[FnArg::new("value", Type::String)],
723 return_type: Type::String,
724 func: example_builtin
725 }
726 )
727 )
728 }
729
730 #[test]
731 fn test_builtins_debug_1_arity() {
732 assert_eq!(
733 "test_builtin(value: String) -> String",
734 format!(
735 "{:#?}",
736 BuiltinFn {
737 name: "test_builtin",
738 args: &[FnArg::new("value", Type::String)],
739 return_type: Type::String,
740 func: example_builtin
741 }
742 )
743 )
744 }
745
746 #[test]
747 fn test_builtins_display_2_arity() {
748 assert_eq!(
749 "test_builtin(a: String, b: String) -> String",
750 format!(
751 "{}",
752 BuiltinFn {
753 name: "test_builtin",
754 args: &[FnArg::new("a", Type::String), FnArg::new("b", Type::String)],
755 return_type: Type::String,
756 func: example_builtin
757 }
758 )
759 )
760 }
761
762 #[test]
763 fn test_builtins_debug_2_arity() {
764 assert_eq!(
765 "test_builtin(a: String, b: String) -> String",
766 format!(
767 "{:#?}",
768 BuiltinFn {
769 name: "test_builtin",
770 args: &[FnArg::new("a", Type::String), FnArg::new("b", Type::String)],
771 return_type: Type::String,
772 func: example_builtin
773 }
774 )
775 )
776 }
777}