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 mut fields: HashMap<Arc<str>, String> = HashMap::new();
61 let primary_field = &self.column_names[self.primary_column];
62 let mut escaped = false;
63 let mut in_field = false;
64 let mut field = None;
65 let mut text = String::new();
66 self.column_ranges.clear();
67 self.column_ranges
68 .push((0..usize::MAX, Some(primary_field.clone())));
69
70 macro_rules! finish_field {
71 () => {
72 let key = field.take().unwrap_or(primary_field);
73
74 let pat = text.strip_suffix(' ').unwrap_or(&text);
78
79 if let Some(pattern) = fields.get_mut(key) {
80 pattern.push(' ');
81 pattern.push_str(pat);
82 } else {
83 fields.insert(key.clone(), pat.to_string());
84 }
85 text.clear();
86 };
87 }
88
89 for (idx, ch) in input.char_indices() {
90 match ch {
91 _ if escaped => {
93 if ch != '%' {
97 text.push('\\');
98 }
99 text.push(ch);
100 escaped = false;
101 }
102 '\\' => escaped = !escaped,
103 '%' => {
104 if !text.is_empty() {
105 finish_field!();
106 }
107 let (range, _field) = self
108 .column_ranges
109 .last_mut()
110 .expect("column_ranges is non-empty");
111 range.end = idx;
112 in_field = true;
113 }
114 ' ' if in_field => {
115 text.clear();
116 in_field = false;
117 if text.is_empty() && self.empty_column {
118 field = self.column_names.iter().find(|x| x.is_empty());
119 }
120 }
121 _ if in_field => {
122 text.push(ch);
123 field = self
126 .column_names
127 .iter()
128 .filter(|col| col.starts_with(&text))
129 .min_by_key(|col| col.len());
131
132 if let Some((_range, current_field)) = self
134 .column_ranges
135 .last_mut()
136 .filter(|(range, _)| range.end == usize::MAX)
137 {
138 *current_field = field.cloned();
139 } else {
140 self.column_ranges.push((idx..usize::MAX, field.cloned()));
141 }
142 }
143 _ => text.push(ch),
144 }
145 }
146
147 if !in_field && !text.is_empty() {
148 finish_field!();
149 }
150
151 let new_inner: HashMap<_, _> = fields
152 .into_iter()
153 .map(|(field, query)| (field, query.as_str().into()))
154 .collect();
155
156 mem::replace(&mut self.inner, new_inner)
157 }
158
159 pub fn active_column(&self, cursor: usize) -> Option<&Arc<str>> {
166 let point = self
167 .column_ranges
168 .partition_point(|(range, _field)| cursor > range.end);
169
170 self.column_ranges
171 .get(point)
172 .filter(|(range, _field)| cursor >= range.start && cursor <= range.end)
173 .and_then(|(_range, field)| field.as_ref())
174 }
175}