axe_cli/
arg_resolver.rs

1use templates_resolver::resolve_template_args;
2use thiserror::Error;
3use tokens::LexingError;
4
5use std::str::Split;
6
7use crate::cli::Cli;
8
9mod templates_resolver;
10mod tokens;
11
12// echo abcd{0}efg{1.0} {2} {}
13#[derive(Debug, PartialEq, Eq)]
14enum ArgTemplatePart<'a> {
15    //{0}
16    Index(usize),
17    //{0.}
18    IndexSplit(usize, &'a str),
19    //{0.0}
20    IndexSplitIndex(usize, &'a str, usize),
21    //{.0}
22    SplitIndex(&'a str, usize),
23    //{.}
24    Split(&'a str),
25    //{}
26    Empty,
27    //abcd
28    FreeText(&'a str),
29}
30
31#[derive(Error, Debug)]
32pub enum ResolveError {
33    //TODO: this needs better error handling because we don't display which arg failed
34    //so maybe anyhow will be better so we can keep adding context?
35    #[error("Index {0} is out of bounds")]
36    InvalidIndex(usize),
37    #[error("unknown data store error")]
38    Other,
39}
40
41//FIXME:template_args may be empty, it means that we should append all args as last argument
42pub fn resolve_cmd_args(stdin_entries: Vec<String>, cli: &Cli) -> Vec<Vec<String>> {
43    let mut entries = Vec::new();
44    //FIXME: handle error
45    let args_resolver = ArgumentResolver::new(&cli.args_templates).unwrap();
46
47    for stdin_entry in stdin_entries {
48        let input_args = stdin_entry
49            .split(&cli.args_separator)
50            .collect::<Vec<&str>>();
51        //FIXME: handle error
52        //we may add flag to choose how to behave on error like:
53        //panic and break
54        //replace invalid value withempty string
55        //ignore failed entry and continue with others
56        let entry = args_resolver.resolve(input_args).unwrap();
57        entries.push(entry);
58    }
59    entries
60}
61
62type ResolvedArgument<'a> = Vec<ArgTemplatePart<'a>>;
63
64struct ArgumentResolver<'a> {
65    resolved_args: Vec<ResolvedArgument<'a>>,
66    has_any_placeholder: bool,
67}
68
69impl<'a> ArgumentResolver<'a> {
70    fn new(arg_templates: &'a [String]) -> Result<ArgumentResolver<'a>, LexingError> {
71        let resolved_args = resolve_template_args(arg_templates)?;
72        let has_any_placeholder = resolved_args.iter().any(|arg_template| {
73            arg_template
74                .iter()
75                .any(|part| !matches!(part, ArgTemplatePart::FreeText(_)))
76        });
77        Ok(ArgumentResolver {
78            resolved_args,
79            has_any_placeholder,
80        })
81    }
82
83    fn resolve(&self, input_args: Vec<&str>) -> Result<Vec<String>, ResolveError> {
84        if !self.has_any_placeholder {
85            return Ok(input_args.into_iter().map(|a| a.to_string()).collect());
86        }
87        let mut result = Vec::new();
88        for arg_template in &self.resolved_args {
89            let mut resolved = self.resolve_arg_template(arg_template, &input_args)?;
90            result.append(&mut resolved);
91        }
92        Ok(result)
93    }
94
95    fn resolve_arg_template(
96        &self,
97        arg_template: &[ArgTemplatePart],
98        input_args: &[&str],
99    ) -> Result<Vec<String>, ResolveError> {
100        let mut resolved = Vec::new();
101        for part in arg_template {
102            let single_part = resolve_single_arg_part(part, input_args)?;
103            resolved = multiply_args_parts(resolved, single_part);
104        }
105        Ok(resolved)
106    }
107}
108
109fn resolve_single_arg_part(
110    arg_template: &ArgTemplatePart,
111    input_args: &[&str],
112) -> Result<Vec<String>, ResolveError> {
113    let resolved = match arg_template {
114        ArgTemplatePart::Index(idx) => vec![get_input_arg(*idx, input_args)?.to_string()],
115        ArgTemplatePart::IndexSplit(idx, split_by) => {
116            let input_arg = get_input_arg(*idx, input_args)?;
117            input_arg.split(*split_by).map(|s| s.to_string()).collect()
118        }
119        ArgTemplatePart::IndexSplitIndex(idx, split_by, split_idx) => {
120            let input_arg = get_input_arg(*idx, input_args)?;
121            let mut splitted = input_arg.split(*split_by);
122            vec![get_split_arg(*split_idx, &mut splitted)?.to_string()]
123        }
124        ArgTemplatePart::SplitIndex(split_by, split_idx) => input_args
125            .iter()
126            .map(|a| {
127                let mut splitted = a.split(*split_by);
128                get_split_arg(*split_idx, &mut splitted).map(|s| s.to_string())
129            })
130            .collect::<Result<Vec<String>, ResolveError>>()?,
131        ArgTemplatePart::Split(split_by) => input_args
132            .iter()
133            .map(|a| a.split(split_by).map(|s| s.to_string()))
134            .flat_map(|a| a.into_iter())
135            .collect::<Vec<String>>(),
136        ArgTemplatePart::Empty => input_args.iter().map(|a| a.to_string()).collect(),
137        ArgTemplatePart::FreeText(text) => vec![text.to_string()],
138    };
139    Ok(resolved)
140}
141
142fn get_input_arg<'a>(idx: usize, input_args: &'a [&'a str]) -> Result<&'a str, ResolveError> {
143    input_args
144        .get(idx)
145        .copied()
146        .ok_or(ResolveError::InvalidIndex(idx))
147}
148
149fn get_split_arg<'a>(
150    idx: usize,
151    splitted: &'a mut Split<&'a str>,
152) -> Result<&'a str, ResolveError> {
153    splitted.nth(idx).ok_or(ResolveError::InvalidIndex(idx))
154}
155
156//This performs args multiplication for example
157//[a,b] * [c] -> [ac,bc]
158//[a] * [b] -> [ab]
159//[a,b] * [c,d] -> [ac,ad,bc,bd]
160fn multiply_args_parts(a: Vec<String>, b: Vec<String>) -> Vec<String> {
161    if a.is_empty() {
162        return b;
163    }
164    if b.is_empty() {
165        return a;
166    }
167    let mut result = Vec::with_capacity(a.len() * b.len());
168    for a_part in a {
169        for b_part in b.clone() {
170            result.push(format!("{}{}", a_part, b_part));
171        }
172    }
173
174    result
175}