forked from PowerShell/DSC
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmod.rs
130 lines (118 loc) · 5.18 KB
/
mod.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use std::collections::HashMap;
use crate::DscError;
use crate::configure::context::Context;
use serde_json::Value;
pub mod add;
pub mod base64;
pub mod concat;
pub mod create_array;
pub mod div;
pub mod envvar;
pub mod mul;
pub mod parameters;
pub mod resource_id;
/// The kind of argument that a function accepts.
#[derive(Debug, PartialEq)]
pub enum AcceptedArgKind {
Array,
Boolean,
Number,
Object,
String,
}
/// A function that can be invoked.
pub trait Function {
/// The minimum number of arguments that the function accepts.
fn min_args(&self) -> usize;
/// The maximum number of arguments that the function accepts.
fn max_args(&self) -> usize;
/// The types of arguments that the function accepts.
fn accepted_arg_types(&self) -> Vec<AcceptedArgKind>;
/// Invoke the function.
///
/// # Arguments
///
/// * `args` - The arguments to the function.
///
/// # Errors
///
/// This function will return an error if the function fails to execute.
fn invoke(&self, args: &[Value], context: &Context) -> Result<Value, DscError>;
}
/// A dispatcher for functions.
pub struct FunctionDispatcher {
functions: HashMap<String, Box<dyn Function>>,
}
impl FunctionDispatcher {
/// Create a new `FunctionDispatcher` instance.
#[must_use]
pub fn new() -> Self {
let mut functions: HashMap<String, Box<dyn Function>> = HashMap::new();
functions.insert("add".to_string(), Box::new(add::Add{}));
functions.insert("base64".to_string(), Box::new(base64::Base64{}));
functions.insert("concat".to_string(), Box::new(concat::Concat{}));
functions.insert("createArray".to_string(), Box::new(create_array::CreateArray{}));
functions.insert("div".to_string(), Box::new(div::Div{}));
functions.insert("envvar".to_string(), Box::new(envvar::Envvar{}));
functions.insert("mul".to_string(), Box::new(mul::Mul{}));
functions.insert("parameters".to_string(), Box::new(parameters::Parameters{}));
functions.insert("resourceId".to_string(), Box::new(resource_id::ResourceId{}));
Self {
functions,
}
}
/// Invoke a function.
///
/// # Arguments
///
/// * `name` - The name of the function to invoke.
/// * `args` - The arguments to the function.
///
/// # Errors
///
/// This function will return an error if the function fails to execute.
pub fn invoke(&self, name: &str, args: &Vec<Value>, context: &Context) -> Result<Value, DscError> {
let Some(function) = self.functions.get(name) else {
return Err(DscError::Parser(format!("Unknown function '{name}'")));
};
// check if arg number are valid
let min_args = function.min_args();
let max_args = function.max_args();
if args.len() < min_args || args.len() > max_args {
if max_args == 0 {
return Err(DscError::Parser(format!("Function '{name}' does not accept arguments")));
}
else if min_args == max_args {
return Err(DscError::Parser(format!("Function '{name}' requires exactly {min_args} arguments")));
}
else if max_args == usize::MAX {
return Err(DscError::Parser(format!("Function '{name}' requires at least {min_args} arguments")));
}
return Err(DscError::Parser(format!("Function '{name}' requires between {min_args} and {max_args} arguments")));
}
// check if arg types are valid
let accepted_arg_types = function.accepted_arg_types();
let accepted_args_string = accepted_arg_types.iter().map(|x| format!("{x:?}")).collect::<Vec<String>>().join(", ");
for value in args {
if value.is_array() && !accepted_arg_types.contains(&AcceptedArgKind::Array) {
return Err(DscError::Parser(format!("Function '{name}' does not accept array arguments, accepted types are: {accepted_args_string}")));
} else if value.is_boolean() && !accepted_arg_types.contains(&AcceptedArgKind::Boolean) {
return Err(DscError::Parser(format!("Function '{name}' does not accept boolean arguments, accepted types are: {accepted_args_string}")));
} else if value.is_number() && !accepted_arg_types.contains(&AcceptedArgKind::Number) {
return Err(DscError::Parser(format!("Function '{name}' does not accept number arguments, accepted types are: {accepted_args_string}")));
} else if value.is_object() && !accepted_arg_types.contains(&AcceptedArgKind::Object) {
return Err(DscError::Parser(format!("Function '{name}' does not accept object arguments, accepted types are: {accepted_args_string}")));
} else if value.is_string() && !accepted_arg_types.contains(&AcceptedArgKind::String) {
return Err(DscError::Parser(format!("Function '{name}' does not accept string argument, accepted types are: {accepted_args_string}")));
}
}
function.invoke(args, context)
}
}
impl Default for FunctionDispatcher {
fn default() -> Self {
Self::new()
}
}