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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
use templates_resolver::resolve_template_args;
use thiserror::Error;
use tokens::LexingError;

use std::str::Split;

use crate::cli::Cli;

mod templates_resolver;
mod tokens;

// echo abcd{0}efg{1.0} {2} {}
#[derive(Debug, PartialEq, Eq)]
enum ArgTemplatePart<'a> {
    //{0}
    Index(usize),
    //{0.}
    IndexSplit(usize, &'a str),
    //{0.0}
    IndexSplitIndex(usize, &'a str, usize),
    //{.0}
    SplitIndex(&'a str, usize),
    //{.}
    Split(&'a str),
    //{}
    Empty,
    //abcd
    FreeText(&'a str),
}

#[derive(Error, Debug)]
pub enum ResolveError {
    //TODO: this needs better error handling because we don't display which arg failed
    //so maybe anyhow will be better so we can keep adding context?
    #[error("Index {0} is out of bounds")]
    InvalidIndex(usize),
    #[error("unknown data store error")]
    Other,
}

//FIXME:template_args may be empty, it means that we should append all args as last argument
pub fn resolve_cmd_args(stdin_entries: Vec<String>, cli: &Cli) -> Vec<Vec<String>> {
    let mut entries = Vec::new();
    //FIXME: handle error
    let args_resolver = ArgumentResolver::new(&cli.args_templates).unwrap();

    for stdin_entry in stdin_entries {
        let input_args = stdin_entry
            .split(&cli.args_separator)
            .collect::<Vec<&str>>();
        //FIXME: handle error
        //we may add flag to choose how to behave on error like:
        //panic and break
        //replace invalid value withempty string
        //ignore failed entry and continue with others
        let entry = args_resolver.resolve(input_args).unwrap();
        entries.push(entry);
    }
    entries
}

type ResolvedArgument<'a> = Vec<ArgTemplatePart<'a>>;

struct ArgumentResolver<'a> {
    resolved_args: Vec<ResolvedArgument<'a>>,
}

impl<'a> ArgumentResolver<'a> {
    fn new(arg_templates: &'a [String]) -> Result<ArgumentResolver<'a>, LexingError> {
        let resolved_args = resolve_template_args(arg_templates)?;
        Ok(ArgumentResolver { resolved_args })
    }

    fn resolve(&self, input_args: Vec<&str>) -> Result<Vec<String>, ResolveError> {
        let mut result = Vec::new();
        for arg_template in &self.resolved_args {
            let mut resolved = self.resolve_arg_template(arg_template, &input_args)?;
            result.append(&mut resolved);
        }
        Ok(result)
    }

    fn resolve_arg_template(
        &self,
        arg_template: &[ArgTemplatePart],
        input_args: &[&str],
    ) -> Result<Vec<String>, ResolveError> {
        let mut resolved = Vec::new();
        for part in arg_template {
            let single_part = resolve_single_arg_part(part, input_args)?;
            resolved = multiply_args_parts(resolved, single_part);
        }
        Ok(resolved)
    }
}

fn resolve_single_arg_part(
    arg_template: &ArgTemplatePart,
    input_args: &[&str],
) -> Result<Vec<String>, ResolveError> {
    let resolved = match arg_template {
        ArgTemplatePart::Index(idx) => vec![get_input_arg(*idx, input_args)?.to_string()],
        ArgTemplatePart::IndexSplit(idx, split_by) => {
            let input_arg = get_input_arg(*idx, input_args)?;
            input_arg.split(*split_by).map(|s| s.to_string()).collect()
        }
        ArgTemplatePart::IndexSplitIndex(idx, split_by, split_idx) => {
            let input_arg = get_input_arg(*idx, input_args)?;
            let mut splitted = input_arg.split(*split_by);
            vec![get_split_arg(*split_idx, &mut splitted)?.to_string()]
        }
        ArgTemplatePart::SplitIndex(split_by, split_idx) => input_args
            .iter()
            .map(|a| {
                let mut splitted = a.split(*split_by);
                get_split_arg(*split_idx, &mut splitted).map(|s| s.to_string())
            })
            .collect::<Result<Vec<String>, ResolveError>>()?,
        ArgTemplatePart::Split(split_by) => input_args
            .iter()
            .map(|a| a.split(split_by).map(|s| s.to_string()))
            .flat_map(|a| a.into_iter())
            .collect::<Vec<String>>(),
        ArgTemplatePart::Empty => input_args.iter().map(|a| a.to_string()).collect(),
        ArgTemplatePart::FreeText(text) => vec![text.to_string()],
    };
    Ok(resolved)
}

fn get_input_arg<'a>(idx: usize, input_args: &'a [&'a str]) -> Result<&'a str, ResolveError> {
    input_args
        .get(idx)
        .copied()
        .ok_or(ResolveError::InvalidIndex(idx))
}

fn get_split_arg<'a>(
    idx: usize,
    splitted: &'a mut Split<&'a str>,
) -> Result<&'a str, ResolveError> {
    splitted.nth(idx).ok_or(ResolveError::InvalidIndex(idx))
}

//This performs args multiplication for example
//[a,b] * [c] -> [ac,bc]
//[a] * [b] -> [ab]
//[a,b] * [c,d] -> [ac,ad,bc,bd]
fn multiply_args_parts(a: Vec<String>, b: Vec<String>) -> Vec<String> {
    if a.is_empty() {
        return b;
    }
    if b.is_empty() {
        return a;
    }
    let mut result = Vec::with_capacity(a.len() * b.len());
    for a_part in a {
        for b_part in b.clone() {
            result.push(format!("{}{}", a_part, b_part));
        }
    }

    result
}