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
|
use {
crate::{
pattern::*,
verb::VerbInvocation,
},
bet::BeTree,
std::fmt,
};
/// An intermediate parsed representation of the raw string
#[derive(Debug, Clone)]
pub struct CommandParts {
pub raw_pattern: String, // may be empty
pub pattern: BeTree<PatternOperator, PatternParts>,
pub verb_invocation: Option<VerbInvocation>, // may be empty if user typed the separator but no char after
}
impl fmt::Display for CommandParts {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.raw_pattern)?;
if let Some(invocation) = &self.verb_invocation {
write!(f, "{}", invocation)?;
}
Ok(())
}
}
impl CommandParts {
pub fn has_not_empty_verb_invocation(&self) -> bool {
self.verb_invocation
.as_ref()
.map_or(false, |vi| !vi.is_empty())
}
pub fn from(mut raw: String) -> Self {
let mut invocation_start_pos: Option<usize> = None;
let mut escaping = false;
let mut pt = BeTree::new();
for (pos, c) in raw.char_indices() {
if c == '\\' {
if escaping {
escaping = false;
} else {
escaping = true;
continue;
}
}
if !escaping {
if c == ' ' || c == ':' {
invocation_start_pos = Some(pos);
break;
}
if c == '/' {
pt.mutate_or_create_atom(PatternParts::default).add_part();
continue;
}
let allow_inter_pattern_token = match pt.current_atom() {
Some(pattern_parts) => pattern_parts.allow_inter_pattern_token(),
None => true,
};
if allow_inter_pattern_token {
match c {
'|' if pt.accept_binary_operator() => {
pt.push_operator(PatternOperator::Or);
continue;
}
'&' if pt.accept_binary_operator() => {
pt.push_operator(PatternOperator::And);
continue;
}
'!' if pt.accept_unary_operator() => {
pt.push_operator(PatternOperator::Not);
continue;
}
'(' if pt.accept_opening_par() => {
pt.open_par();
continue;
}
')' if pt.accept_closing_par() => {
pt.close_par();
continue;
}
_ => {}
}
}
}
pt.mutate_or_create_atom(PatternParts::default).push(c);
escaping = false;
}
let mut verb_invocation = None;
if let Some(pos) = invocation_start_pos {
verb_invocation = Some(VerbInvocation::from(&raw[pos + 1..]));
raw.truncate(pos);
}
CommandParts {
raw_pattern: raw,
pattern: pt,
verb_invocation,
}
}
/// split an input into its two possible parts, the pattern
/// and the verb invocation. Each part, when defined, is
/// suitable to create a command on its own.
pub fn split(mut self) -> (Option<CommandParts>, Option<CommandParts>) {
let verb_invocation = self.verb_invocation.take();
(
if self.raw_pattern.is_empty() {
None
} else {
Some(CommandParts {
raw_pattern: self.raw_pattern,
pattern: self.pattern,
verb_invocation: None,
})
},
verb_invocation.map(|verb_invocation| CommandParts {
raw_pattern: String::new(),
pattern: BeTree::new(),
verb_invocation: Some(verb_invocation),
}),
)
}
}
|