cli/functions/
mod.rs

1//! # functions
2//!
3//! Custom operations which can be invoked in makefiles.
4//!
5
6#[cfg(test)]
7#[path = "mod_test.rs"]
8mod mod_test;
9
10mod decode_func;
11mod getat_func;
12mod remove_empty_func;
13mod split_func;
14mod trim_func;
15
16use crate::error::CargoMakeError;
17use crate::types::{Step, Task};
18
19fn run_function(
20    function_name: &str,
21    function_args: &Vec<String>,
22) -> Result<Vec<String>, CargoMakeError> {
23    debug!(
24        "Running function: {} arguments: {:#?}",
25        &function_name, &function_args
26    );
27
28    match function_name {
29        "split" => Ok(split_func::invoke(function_args)),
30        "remove-empty" => Ok(remove_empty_func::invoke(function_args)),
31        "trim" => trim_func::invoke(function_args),
32        "getat" => Ok(getat_func::invoke(function_args)),
33        "decode" => Ok(decode_func::invoke(function_args)),
34        _ => {
35            error!("Unknown function: {}", &function_name);
36            Err(CargoMakeError::NotFound(format!(
37                "Unknown function: {}",
38                &function_name
39            )))
40        }
41    }
42}
43
44fn get_function_name(function_string: &str) -> Option<String> {
45    match function_string.find('(') {
46        Some(index) => Some(function_string[0..index].to_string()),
47        None => None,
48    }
49}
50
51fn get_function_argument(value: &str) -> String {
52    let str_value = if value.len() == 1 {
53        value
54    } else {
55        value.trim()
56    };
57
58    str_value.to_string()
59}
60
61fn get_function_arguments(function_string: &str) -> Option<Vec<String>> {
62    if function_string.starts_with("(") && function_string.ends_with(")") {
63        let args_string = function_string[1..(function_string.len() - 1)].to_string();
64
65        let arguments = if args_string.len() > 0 {
66            args_string
67                .split(",")
68                .map(|str_value| get_function_argument(&str_value))
69                .collect()
70        } else {
71            vec![]
72        };
73
74        Some(arguments)
75    } else {
76        None
77    }
78}
79
80fn evaluate_and_run(value: &str) -> Result<Vec<String>, CargoMakeError> {
81    let value_string = value.to_string();
82
83    if value_string.starts_with("@@") {
84        let mut function_string = value[2..].to_string();
85
86        let func_name_option = get_function_name(&function_string);
87        match func_name_option {
88            Some(function_name) => {
89                function_string = function_string[function_name.len()..].to_string();
90                let func_args_option = get_function_arguments(&function_string);
91
92                match func_args_option {
93                    Some(function_args) => run_function(&function_name, &function_args),
94                    None => Ok(vec![value_string]),
95                }
96            }
97            None => Ok(vec![value_string]),
98        }
99    } else {
100        Ok(vec![value_string])
101    }
102}
103
104fn modify_arguments(task: &mut Task) -> Result<(), CargoMakeError> {
105    task.args = match task.args {
106        Some(ref args) => {
107            let mut new_args = vec![];
108
109            for index in 0..args.len() {
110                let result_args = evaluate_and_run(&args[index])?;
111
112                for result_index in 0..result_args.len() {
113                    new_args.push(result_args[result_index].clone());
114                }
115            }
116
117            Some(new_args)
118        }
119        None => None,
120    };
121    Ok(())
122}
123
124pub(crate) fn run(step: &Step) -> Result<Step, CargoMakeError> {
125    //clone data before modify
126    let mut config = step.config.clone();
127
128    //update args by running any needed function
129    modify_arguments(&mut config)?;
130
131    Ok(Step {
132        name: step.name.clone(),
133        config,
134    })
135}