matchmaker/nucleo/
query.rs1use std::{collections::HashMap, mem, ops::Range, sync::Arc};
5
6pub struct PickerQuery {
7 column_names: Box<[Arc<str>]>,
9 primary_column: usize,
13 inner: HashMap<Arc<str>, Arc<str>>,
16 column_ranges: Vec<(Range<usize>, Option<Arc<str>>)>,
20
21 empty_column: bool,
22}
23
24impl PartialEq<HashMap<Arc<str>, Arc<str>>> for PickerQuery {
25 fn eq(&self, other: &HashMap<Arc<str>, Arc<str>>) -> bool {
26 self.inner.eq(other)
27 }
28}
29
30impl PickerQuery {
31 pub fn new<I: Iterator<Item = Arc<str>>>(column_names: I, primary_column: usize) -> Self {
32 let column_names: Box<[_]> = column_names.collect();
33 let inner = HashMap::with_capacity(column_names.len());
34 let column_ranges = vec![(0..usize::MAX, Some(column_names[primary_column].clone()))];
35 let empty_column = column_names.iter().any(|c| c.is_empty());
36
37 Self {
38 column_names,
39 primary_column,
40 inner,
41 column_ranges,
42 empty_column,
43 }
44 }
45
46 pub fn get(&self, column: &str) -> Option<&Arc<str>> {
47 self.inner.get(column)
48 }
49
50 pub fn primary_column_query(&self) -> Option<&str> {
51 let name = self.column_names.get(self.primary_column)?;
52 self.inner.get(name).map(|s| &**s)
53 }
54
55 pub fn primary_column_name(&self) -> Option<&str> {
56 self.column_names.get(self.primary_column).map(|s| &**s)
57 }
58
59 pub fn parse(&mut self, input: &str) -> HashMap<Arc<str>, Arc<str>> {
60 let (new_inner, new_ranges) = self.parse_impl(input);
61 self.column_ranges = new_ranges;
62 mem::replace(&mut self.inner, new_inner)
63 }
64
65 fn parse_impl(
66 &self,
67 input: &str,
68 ) -> (
69 HashMap<Arc<str>, Arc<str>>,
70 Vec<(std::ops::Range<usize>, Option<Arc<str>>)>,
71 ) {
72 let mut fields: std::collections::HashMap<Arc<str>, String> =
73 std::collections::HashMap::new();
74 let primary_field = &self.column_names[self.primary_column];
75 let mut column_ranges: Vec<(std::ops::Range<usize>, Option<Arc<str>>)> =
76 vec![(0..usize::MAX, Some(primary_field.clone()))];
77
78 let mut escaped = false;
79 let mut in_field = false;
80 let mut field = None;
81 let mut text = String::new();
82
83 macro_rules! finish_field {
84 () => {
85 let key = field.take().unwrap_or(primary_field);
86
87 let pat = text.strip_suffix(' ').unwrap_or(&text);
91
92 if let Some(pattern) = fields.get_mut(key) {
93 pattern.push(' ');
94 pattern.push_str(pat);
95 } else {
96 fields.insert(key.clone(), pat.to_string());
97 }
98 text.clear();
99 };
100 }
101
102 for (idx, ch) in input.char_indices() {
103 match ch {
104 _ if escaped => {
106 if ch != '%' {
110 text.push('\\');
111 }
112 text.push(ch);
113 escaped = false;
114 }
115 '\\' => escaped = !escaped,
116 '%' => {
117 if !text.is_empty() {
118 finish_field!();
119 }
120 if let Some((range, _)) = column_ranges.last_mut() {
122 range.end = idx;
123 }
124 in_field = true;
125 }
126 ' ' if in_field => {
127 text.clear();
128 in_field = false;
129 if text.is_empty() && self.empty_column {
131 field = self.column_names.iter().find(|x| x.is_empty());
132 }
133 }
134 _ if in_field => {
135 text.push(ch);
136 field = self
139 .column_names
140 .iter()
141 .filter(|col| col.starts_with(&text))
142 .min_by_key(|col| col.len());
144
145 if let Some((_range, current_field)) = column_ranges
147 .last_mut()
148 .filter(|(range, _)| range.end == usize::MAX)
149 {
150 *current_field = field.cloned();
151 } else {
152 column_ranges.push((idx..usize::MAX, field.cloned()));
153 }
154 }
155 _ => text.push(ch),
156 }
157 }
158
159 if !in_field && !text.is_empty() {
161 finish_field!();
162 }
163
164 let new_fields: HashMap<Arc<str>, Arc<str>> = fields
165 .into_iter()
166 .map(|(field, query)| (field, query.into()))
167 .collect();
168
169 (new_fields, column_ranges)
170 }
171
172 pub fn current_column(&self, cursor: usize) -> Option<&Arc<str>> {
179 let point = self
180 .column_ranges
181 .partition_point(|(range, _field)| cursor > range.end);
182
183 self.column_ranges
184 .get(point)
185 .filter(|(range, _field)| cursor >= range.start && cursor <= range.end)
186 .and_then(|(_range, field)| field.as_ref())
187 }
188
189 pub fn active_column_name(&self, input: &str) -> String {
190 let (_, ranges) = self.parse_impl(input);
191 ranges
192 .last()
193 .and_then(|x| x.1.as_deref())
194 .unwrap_or(self.column_names[self.primary_column].as_ref())
195 .to_string()
196 }
197
198 pub fn active_column_index(&self, cursor: usize) -> usize {
212 self.current_column(cursor)
213 .and_then(|name| self.column_names.iter().position(|c| c == name))
214 .unwrap_or(self.primary_column)
215 }
216
217 pub fn with_default_column(mut self, index: usize) -> Self {
222 self.primary_column = index;
223 let primary_field = &self.column_names[self.primary_column];
224 self.column_ranges.clear();
225 self.column_ranges
226 .push((0..usize::MAX, Some(primary_field.clone())));
227 self
228 }
229}