txtx_addon_kit/types/
functions.rs1use super::{
2 diagnostics::Diagnostic,
3 types::{Type, Value},
4 AuthorizationContext,
5};
6
7#[derive(Clone, Debug)]
8pub struct FunctionInput {
9 pub name: String,
10 pub documentation: String,
11 pub typing: Vec<Type>,
12 pub optional: bool,
13}
14
15#[derive(Clone, Debug)]
16pub struct FunctionOutput {
17 pub documentation: String,
18 pub typing: Type,
19}
20
21#[derive(Clone, Debug)]
22pub struct FunctionSpecification {
23 pub name: String,
24 pub documentation: String,
25 pub inputs: Vec<FunctionInput>,
26 pub output: FunctionOutput,
27 pub example: String,
28 pub snippet: String,
29 pub runner: FunctionRunner,
30 pub checker: FunctionChecker,
31}
32
33type FunctionRunner =
34 fn(&FunctionSpecification, &AuthorizationContext, &Vec<Value>) -> Result<Value, Diagnostic>;
35type FunctionChecker =
36 fn(&FunctionSpecification, &AuthorizationContext, &Vec<Type>) -> Result<Type, Diagnostic>;
37
38pub trait FunctionImplementation {
39 fn check_instantiability(
40 _fn_spec: &FunctionSpecification,
41 _auth_ctx: &AuthorizationContext,
42 _args: &Vec<Type>,
43 ) -> Result<Type, Diagnostic>;
44
45 fn run(
46 _fn_spec: &FunctionSpecification,
47 _auth_ctx: &AuthorizationContext,
48 _args: &Vec<Value>,
49 ) -> Result<Value, Diagnostic>;
50}
51
52pub fn fn_diag_with_ctx(
53 namespace: String,
54) -> impl Fn(&FunctionSpecification, String) -> Diagnostic {
55 let fn_diag_with_ctx = move |fn_spec: &FunctionSpecification, e: String| -> Diagnostic {
56 Diagnostic::error_from_string(format!("function '{}::{}': {}", namespace, fn_spec.name, e))
57 };
58 return fn_diag_with_ctx;
59}
60
61pub fn arg_checker_with_ctx(
62 namespace: String,
63) -> impl Fn(&FunctionSpecification, &Vec<Value>) -> Result<(), Diagnostic> {
64 let fn_checker =
65 move |fn_spec: &FunctionSpecification, args: &Vec<Value>| -> Result<(), Diagnostic> {
66 for (i, input) in fn_spec.inputs.iter().enumerate() {
67 if !input.optional {
68 if let Some(arg) = args.get(i) {
69 let mut has_type_match = false;
70 for typing in input.typing.iter() {
71 let arg_type = arg.get_type();
72 if let Type::Addon(_) = arg_type {
75 if let Type::Addon(_) = typing {
76 has_type_match = true;
77 break;
78 }
79 }
80 if let Type::Array(_) = arg_type {
82 if arg.expect_array().len() == 0 {
83 has_type_match = true;
84 break;
85 }
86 }
87 if let Type::Array(inner) = typing {
89 if let Type::Null(_) = **inner {
90 has_type_match = true;
91 break;
92 }
93 }
94 if arg_type.eq(typing) {
95 has_type_match = true;
96 break;
97 }
98 }
99 if !has_type_match {
100 let expected_types = input
101 .typing
102 .iter()
103 .map(|t| t.to_string())
104 .collect::<Vec<String>>()
105 .join(",");
106 return Err(Diagnostic::error_from_string(format!(
107 "function '{}::{}' argument #{} ({}) should be of type ({}), found {}",
108 namespace,
109 fn_spec.name,
110 i + 1,
111 input.name,
112 expected_types,
113 arg.get_type().to_string()
114 )));
115 }
116 } else {
117 return Err(Diagnostic::error_from_string(format!(
118 "function '{}::{}' missing required argument #{} ({})",
119 namespace,
120 fn_spec.name,
121 i + 1,
122 input.name,
123 )));
124 }
125 }
126 }
127 Ok(())
128 };
129 return fn_checker;
130}