echo_library/common/
starlark_modules.rs1use super::*;
2use anyhow::anyhow;
3#[allow(clippy::type_complexity)]
4#[starlark_module]
5pub fn starlark_workflow_module(builder: &mut GlobalsBuilder) {
6 fn task(
24 kind: String,
25 action_name: String,
26 input_arguments: Value,
27 attributes: Option<Value>,
28 operation: Option<Value>,
29 depend_on: Option<Value>,
30 ) -> anyhow::Result<Task> {
31 if kind == "openwhisk" || kind == "polkadot" {
32 if attributes.is_none() {
33 return Err(anyhow!("Attributes are mandatory for kind: openwhisk or polkadot"));
34 }
35 }
36
37 let mut input_arguments: Vec<Input> = serde_json::from_str(&input_arguments.to_json()?)
38 .map_err(|err| anyhow!("Failed to parse input arguments: {}", err))?;
39
40 let attributes: HashMap<String, String> = match attributes {
41 Some(attributes) => serde_json::from_str(&attributes.to_json()?)
42 .map_err(|err| anyhow!("Failed to parse the attributes: {}", err))?,
43 _ => HashMap::default(),
44 };
45
46 let depend_on: Vec<Depend> = match depend_on {
47 Some(val) => serde_json::from_str(&val.to_json()?)
48 .map_err(|err| anyhow!("Failed to parse depend-on: {}", err))?,
49 None => Vec::default(),
50 };
51
52 for depend in depend_on.iter() {
53 for argument in input_arguments.iter_mut() {
54 if argument.name == depend.cur_field {
55 argument.is_depend = true;
56 break;
57 }
58 }
59 }
60
61 let operation: Operation = match operation {
62 Some(op) => serde_json::from_str(&op.to_json()?)
63 .map_err(|err| anyhow!("Failed to parse the task operation value: {}", err))?,
64 _ => Operation::Normal,
65 };
66
67 Ok(Task {
68 kind,
69 action_name,
70 input_arguments,
71 attributes,
72 operation,
73 depend_on,
74 })
75 }
76
77 fn workflows(
93 name: String,
94 version: String,
95 tasks: Value,
96 eval: &mut Evaluator,
97 ) -> anyhow::Result<Workflow> {
98 let tasks: Vec<Task> = serde_json::from_str(&tasks.to_json()?)
99 .map_err(|err| anyhow!("Failed to parse task value: {}", err))?;
100
101 let mut task_hashmap = HashMap::new();
102
103 for task in tasks {
104 if task_hashmap.contains_key(&task.action_name) {
105 return Err(Error::msg("Duplicate tasks, Task names must be unique"));
106 } else {
107 task_hashmap.insert(task.action_name.clone(), task);
108 }
109 }
110
111 eval.extra
112 .as_ref()
113 .and_then(|extra| extra.downcast_ref::<Composer>())
114 .ok_or_else(|| anyhow!("Failed to obtain Composer from Evaluator"))?
115 .add_workflow(name.clone(), version.clone(), task_hashmap.clone())
116 .map_err(|err| anyhow!("Failed to add workflow: {}", err))?;
117
118 Ok(Workflow {
119 name,
120 version,
121 tasks: task_hashmap,
122 })
123 }
124
125 fn argument(
138 name: String,
139 input_type: Value,
140 default_value: Option<Value>,
141 ) -> anyhow::Result<Input> {
142 let input_type: RustType = serde_json::from_str(&input_type.to_json()?)
143 .map_err(|err| anyhow!("Failed to parse input arguments: {}", err))?;
144
145 let default_value: Option<String> = match default_value {
146 Some(value) => {
147 let value_str = value
148 .to_json()
149 .map_err(|err| anyhow!("Failed to parse default value: {}", err))?;
150
151 match input_type {
152 RustType::String => {
153 if !value_str.contains("\"") {
154 return Err(anyhow!("Value must be in String type"));
155 }
156 }
157 RustType::Int => {
158 if value_str.parse::<i32>().is_err() {
159 return Err(anyhow!("Value must be an integer"));
160 }
161 }
162 RustType::Float => {
163 if value_str.parse::<f32>().is_err() {
164 return Err(anyhow!("Value must be a float"));
165 }
166 }
167 RustType::Uint => {
168 if value_str.parse::<u32>().is_err() {
169 return Err(anyhow!("Value must be a positive integer"));
170 }
171 }
172 RustType::Boolean => {
173 if value_str != "true" && value_str != "false" {
174 return Err(anyhow!("Value must be either true or false"));
175 }
176 }
177
178 _ => return Err(anyhow!("Unsupported input type for default value")),
179 }
180
181 Some(value_str)
182 }
183 None => Default::default(),
184 };
185
186 Ok(Input {
187 name,
188 input_type,
189 default_value,
190 is_depend: false,
191 })
192 }
193
194 fn depend(task_name: String, cur_field: String, prev_field: String) -> anyhow::Result<Depend> {
195 Ok(Depend {
196 task_name,
197 cur_field,
198 prev_field,
199 })
200 }
201
202 fn EchoStruct(name: String, fields: Value, eval: &mut Evaluator) -> anyhow::Result<RustType> {
216 let fields: HashMap<String, RustType> = serde_json::from_str(&fields.to_json()?)
217 .map_err(|err| anyhow!("Failed to parse fields: {}", err))?;
218
219 let composer = eval
220 .extra
221 .as_ref()
222 .and_then(|extra| extra.downcast_ref::<Composer>())
223 .ok_or_else(|| anyhow!("Failed to obtain Composer from Evaluator"))?;
224 let name = name.to_case(Case::Pascal);
225
226 let mut build_string = Vec::new();
227
228 for (key, value) in fields {
229 build_string.push(format!("{}:{}", key, value));
230 }
231
232 let build_string = format!("[{}]", build_string.join(","));
233
234 composer
235 .custom_types
236 .borrow_mut()
237 .insert(
238 name.to_string(),
239 format!(
240 "make_input_struct!(\n{},\n{},\n[Default, Clone, Debug, Deserialize, Serialize]\n);",
241 &name,
242 build_string
243 ));
244
245 Ok(RustType::Struct(name))
246 }
247}
248
249#[starlark_module]
250pub fn starlark_datatype_module(builder: &mut GlobalsBuilder) {
251 fn Tuple(type_1: Value, type_2: Value) -> anyhow::Result<RustType> {
264 let type_1: RustType = serde_json::from_str(&type_1.to_json()?)
265 .map_err(|err| anyhow!("Failed to parse values: {}", err))?;
266 let type_2: RustType = serde_json::from_str(&type_2.to_json()?)
267 .map_err(|err| anyhow!("Failed to parse values: {}", err))?;
268
269 Ok(RustType::Tuple(Box::new(type_1), Box::new(type_2)))
270 }
271
272 fn HashMap(type_1: Value, type_2: Value) -> anyhow::Result<RustType> {
285 let type_1: RustType = serde_json::from_str(&type_1.to_json()?)
286 .map_err(|err| anyhow!("Failed to parse values: {}", err))?;
287 let type_2: RustType = serde_json::from_str(&type_2.to_json()?)
288 .map_err(|err| anyhow!("Failed to parse values: {}", err))?;
289
290 Ok(RustType::HashMap(Box::new(type_1), Box::new(type_2)))
291 }
292
293 fn List(type_of: Value) -> anyhow::Result<RustType> {
305 let type_of: RustType = serde_json::from_str(&type_of.to_json()?)
306 .map_err(|err| anyhow!("Failed to parse values: {}", err))?;
307 Ok(RustType::List(Box::new(type_of)))
308 }
309}
310
311#[starlark_module]
312pub fn starlark_operation_module(builder: &mut GlobalsBuilder) {
313 fn normal() -> anyhow::Result<Operation> {
321 Ok(Operation::Normal)
322 }
323
324 fn concat() -> anyhow::Result<Operation> {
332 Ok(Operation::Concat)
333 }
334
335 fn combine() -> anyhow::Result<Operation> {
343 Ok(Operation::Combine)
344 }
345
346 fn map(field: String) -> anyhow::Result<Operation> {
358 Ok(Operation::Map(field))
359 }
360}