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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
//! # arg_parser
//!
//! Module about argument parsing
use crate::consts::{ESCAPE_CHAR, LIT_CHAR};
use std::{iter::Peekable, str::Chars};
/// Argument parser
pub struct ArgParser {
values: Vec<String>,
previous: Option<char>,
lit_count: usize,
paren_count: usize,
no_previous: bool,
strip_literal: bool,
}
impl ArgParser {
/// Create a new instance
pub(crate) fn new() -> Self {
Self {
values: vec![],
previous: None,
lit_count: 0,
paren_count: 0,
no_previous: false,
strip_literal: true,
}
}
/// Reset variables
fn reset(&mut self) {
self.values.clear();
self.previous = None;
self.lit_count = 0;
self.no_previous = false;
}
/// Don't strip literals
pub(crate) fn no_strip(mut self) -> Self {
self.strip_literal = false;
self
}
/// Don't strip literals
pub(crate) fn set_strip(&mut self, strip_literal: bool) {
self.strip_literal = strip_literal;
}
/// Simply strip literal chunk
pub(crate) fn strip(&mut self, args: &str) -> String {
self.args_to_vec(args, ',', SplitVariant::Greedy)[0].to_owned()
}
/// Check if given length is qualified for given raw arguments
///
/// If length is qualified it returns vector of arguments
/// if not, "None" is returned instead.
pub(crate) fn args_with_len(&mut self, args: &str, length: usize) -> Option<Vec<String>> {
self.reset();
let split_var = if length > 1 {
SplitVariant::Deterred(length - 1)
} else {
// SplitVariant::Greedy
return Some(vec![args.to_string()]);
};
let args: Vec<_> = self.args_to_vec(args, ',', split_var);
if args.len() < length {
return None;
}
Some(args)
}
/// Split raw arguments into a vector
pub(crate) fn args_to_vec(
&mut self,
arg_values: &str,
delimiter: char,
mut split_var: SplitVariant,
) -> Vec<String> {
self.reset();
let mut value = String::new();
let mut arg_iter = arg_values.chars().peekable();
// Return empty vector without going through logics
if arg_values.is_empty() {
return vec![];
}
while let Some(ch) = arg_iter.next() {
// Check parenthesis
self.check_parenthesis(&mut value, ch);
// Greedy = No split
if let SplitVariant::Greedy = split_var {
value.push(ch);
} else if ch == delimiter {
self.branch_delimiter(ch, &mut value, &mut split_var);
} else if ch == ESCAPE_CHAR {
self.branch_escape_char(ch, &mut value, arg_iter.peek());
} else {
// This pushes value in the end, so use continue not push the value
if ch == LIT_CHAR {
// '*'
self.branch_literal_char(ch, &mut value, &mut arg_iter);
} else {
// Non literal character are just pushed
value.push(ch);
}
}
if self.no_previous {
self.previous.replace('0');
self.no_previous = false;
} else {
self.previous.replace(ch);
}
} // while end
// Add last arg
self.values.push(value);
std::mem::take(&mut self.values)
}
/// Check parenthesis for sensible splitting
fn check_parenthesis(&mut self, value: &mut String, ch: char) {
if self.previous.unwrap_or('0') == ESCAPE_CHAR && (ch == '(' || ch == ')') {
value.pop();
self.previous.replace('0');
} else if ch == '(' {
self.paren_count += 1;
} else if ch == ')' && self.paren_count > 0 {
self.paren_count -= 1;
}
}
// ----------
// <BRANCH>
// Start of branch methods
/// Branch on delimiter found
fn branch_delimiter(&mut self, ch: char, value: &mut String, variant: &mut SplitVariant) {
// Either literal or escaped
if self.lit_count > 0 {
value.push(ch);
} else if self.previous.unwrap_or('0') == ESCAPE_CHAR {
value.pop();
value.push(ch);
} else if self.paren_count > 0 {
// If quote is inside parenthesis, simply push it into a value
value.push(ch);
} else {
// not literal
match variant {
SplitVariant::Deterred(count) => {
// move to next value
self.values.push(std::mem::take(value));
let count = *count - 1;
if count > 0 {
*variant = SplitVariant::Deterred(count);
} else {
*variant = SplitVariant::Greedy;
}
self.no_previous = true;
}
// Push everything to current item, index, value or you name it
SplitVariant::Greedy => {
value.push(ch);
}
SplitVariant::Never => {
// move to next value
self.values.push(std::mem::take(value));
}
} // Match end
} // else end
}
/// Branch on escape character found
fn branch_escape_char(&mut self, ch: char, value: &mut String, next: Option<&char>) {
if self.previous.unwrap_or(' ') == ESCAPE_CHAR {
self.no_previous = true;
} else if let Some(&LIT_CHAR) = next {
if !self.strip_literal || self.lit_count > 0 {
value.push(ch);
}
// if next is literal character and previous was not a escape character
// Do nothing
} else {
// If literal print everything without escaping
// or next is anything simply add
value.push(ch);
}
} // end function
/// Branch on literal character found
fn branch_literal_char(
&mut self,
ch: char,
value: &mut String,
arg_iter: &mut Peekable<Chars>,
) {
if self.previous.unwrap_or('0') == ESCAPE_CHAR {
self.lit_count += 1;
// If lit character was given inside literal
// e.g. \* '\*' *\ -> the one inside quotes
if self.lit_count > 1 {
value.push(ch);
}
// First lit character in given args
// Simply ignore character and don't set previous
else {
self.no_previous = true;
if !self.strip_literal {
value.push(ch);
}
}
} else if let Some(&ch_next) = arg_iter.peek() {
// Next is escape char and not inside lit_count
// *\
if ch_next == ESCAPE_CHAR && self.lit_count >= 1 {
self.lit_count -= 1;
arg_iter.next(); // Conume next escape_char
// Lit end was outter most one
if self.lit_count == 0 {
self.no_previous = true;
if !self.strip_literal {
value.push(LIT_CHAR);
value.push(ESCAPE_CHAR);
}
}
// Inside other literal rules
else {
value.push(LIT_CHAR);
value.push(ESCAPE_CHAR);
self.no_previous = true;
}
}
// When *\ Comes first without matching pair
// This is just a string without any meaning
else {
value.push(ch);
}
}
// Meaningless literal charcter are just pushed
else {
value.push(ch);
}
} // end function
// End of branch methods
// </BRANCH>
// ----------
}
/// State indicates whether argument should be parsed greedily or not
#[derive(Debug)]
pub enum SplitVariant {
/// Split argument with given amount
Deterred(usize),
Greedy,
Never,
}