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 parse(&mut self, input: &str) -> HashMap<Arc<str>, Arc<str>> {
51 let mut fields: HashMap<Arc<str>, String> = HashMap::new();
52 let primary_field = &self.column_names[self.primary_column];
53 let mut escaped = false;
54 let mut in_field = false;
55 let mut field = None;
56 let mut text = String::new();
57 self.column_ranges.clear();
58 self.column_ranges
59 .push((0..usize::MAX, Some(primary_field.clone())));
60
61 macro_rules! finish_field {
62 () => {
63 let key = field.take().unwrap_or(primary_field);
64
65 let pat = text.strip_suffix(' ').unwrap_or(&text);
69
70 if let Some(pattern) = fields.get_mut(key) {
71 pattern.push(' ');
72 pattern.push_str(pat);
73 } else {
74 fields.insert(key.clone(), pat.to_string());
75 }
76 text.clear();
77 };
78 }
79
80 for (idx, ch) in input.char_indices() {
81 match ch {
82 _ if escaped => {
84 if ch != '%' {
88 text.push('\\');
89 }
90 text.push(ch);
91 escaped = false;
92 }
93 '\\' => escaped = !escaped,
94 '%' => {
95 if !text.is_empty() {
96 finish_field!();
97 }
98 let (range, _field) = self
99 .column_ranges
100 .last_mut()
101 .expect("column_ranges is non-empty");
102 range.end = idx;
103 in_field = true;
104 }
105 ' ' if in_field => {
106 text.clear();
107 in_field = false;
108 if text.is_empty() && self.empty_column {
109 field = self.column_names.iter().find(|x| x.is_empty());
110 }
111 }
112 _ if in_field => {
113 text.push(ch);
114 field = self
117 .column_names
118 .iter()
119 .filter(|col| col.starts_with(&text))
120 .min_by_key(|col| col.len());
122
123 if let Some((_range, current_field)) = self
125 .column_ranges
126 .last_mut()
127 .filter(|(range, _)| range.end == usize::MAX)
128 {
129 *current_field = field.cloned();
130 } else {
131 self.column_ranges.push((idx..usize::MAX, field.cloned()));
132 }
133 }
134 _ => text.push(ch),
135 }
136 }
137
138 if !in_field && !text.is_empty() {
139 finish_field!();
140 }
141
142 let new_inner: HashMap<_, _> = fields
143 .into_iter()
144 .map(|(field, query)| (field, query.as_str().into()))
145 .collect();
146
147 mem::replace(&mut self.inner, new_inner)
148 }
149
150 pub fn active_column(&self, cursor: usize) -> Option<&Arc<str>> {
157 let point = self
158 .column_ranges
159 .partition_point(|(range, _field)| cursor > range.end);
160
161 self.column_ranges
162 .get(point)
163 .filter(|(range, _field)| cursor >= range.start && cursor <= range.end)
164 .and_then(|(_range, field)| field.as_ref())
165 }
166}