osp_cli/dsl/parse/
quick.rs1use super::key_spec::{ExactMode, KeySpec};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum QuickScope {
9 KeyOrValue,
10 KeyOnly,
11 ValueOnly,
12}
13
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct QuickSpec {
16 pub scope: QuickScope,
17 pub key_spec: KeySpec,
18 pub key_not_equals: bool,
19}
20
21pub fn parse_quick_spec(input: &str) -> QuickSpec {
27 let mut remaining = input.trim();
28 let mut scope: Option<QuickScope> = None;
29 let mut negated = false;
30 let mut existence = false;
31 let mut exact = ExactMode::None;
32 let mut strict_ambiguous = false;
33
34 loop {
35 if let Some(rest) = remaining.strip_prefix("!=") {
36 negated = true;
37 exact = ExactMode::CaseInsensitive;
38 remaining = rest.trim_start();
39 continue;
40 }
41 if let Some(rest) = remaining.strip_prefix("==") {
42 exact = ExactMode::CaseSensitive;
43 strict_ambiguous = true;
44 remaining = rest.trim_start();
45 continue;
46 }
47 if let Some(rest) = remaining.strip_prefix('!') {
48 negated = !negated;
49 remaining = rest.trim_start();
50 continue;
51 }
52 if let Some(rest) = remaining.strip_prefix('?') {
53 existence = true;
54 remaining = rest.trim_start();
55 continue;
56 }
57 if let Some(rest) = remaining.strip_prefix('=') {
58 exact = ExactMode::CaseInsensitive;
59 remaining = rest.trim_start();
60 continue;
61 }
62 if scope.is_none() {
63 let mut chars = remaining.chars();
64 let Some(first) = chars.next() else {
65 break;
66 };
67 let scope_candidate = match first {
68 'K' | 'k' => Some(QuickScope::KeyOnly),
69 'V' | 'v' => Some(QuickScope::ValueOnly),
70 _ => None,
71 };
72 if let Some(scope_value) = scope_candidate {
73 scope = Some(scope_value);
74 let rest = &remaining[first.len_utf8()..];
75 remaining = rest.trim_start();
76 continue;
77 }
78 }
79 break;
80 }
81
82 let scope = scope.unwrap_or(QuickScope::KeyOrValue);
83 let mut key_not_equals = false;
84 if matches!(scope, QuickScope::KeyOnly) && negated && exact == ExactMode::CaseInsensitive {
85 key_not_equals = true;
86 negated = false;
87 }
88
89 let key_spec = KeySpec {
90 token: remaining.trim().to_string(),
91 negated,
92 existence,
93 exact,
94 strict_ambiguous,
95 };
96
97 QuickSpec {
98 scope,
99 key_spec,
100 key_not_equals,
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::{QuickScope, parse_quick_spec};
107
108 #[test]
109 fn parses_key_scope() {
110 let parsed = parse_quick_spec("K uid");
111 assert_eq!(parsed.scope, QuickScope::KeyOnly);
112 assert_eq!(parsed.key_spec.token, "uid");
113 }
114
115 #[test]
116 fn parses_value_scope() {
117 let parsed = parse_quick_spec("V oistes");
118 assert_eq!(parsed.scope, QuickScope::ValueOnly);
119 assert_eq!(parsed.key_spec.token, "oistes");
120 }
121
122 #[test]
123 fn parses_key_not_equals_form() {
124 let parsed = parse_quick_spec("K !=uid");
125 assert!(parsed.key_not_equals);
126 assert_eq!(parsed.key_spec.token, "uid");
127 }
128}