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
22impl PartialEq<HashMap<Arc<str>, Arc<str>>> for PickerQuery {
23 fn eq(&self, other: &HashMap<Arc<str>, Arc<str>>) -> bool {
24 self.inner.eq(other)
25 }
26}
27
28impl PickerQuery {
29 pub fn new<I: Iterator<Item = Arc<str>>>(column_names: I, primary_column: usize) -> Self {
30 let column_names: Box<[_]> = column_names.collect();
31 let inner = HashMap::with_capacity(column_names.len());
32 let column_ranges = vec![(0..usize::MAX, Some(column_names[primary_column].clone()))];
33 Self {
34 column_names,
35 primary_column,
36 inner,
37 column_ranges,
38 }
39 }
40
41 pub fn get(&self, column: &str) -> Option<&Arc<str>> {
42 self.inner.get(column)
43 }
44
45 pub fn parse(&mut self, input: &str) -> HashMap<Arc<str>, Arc<str>> {
46 let mut fields: HashMap<Arc<str>, String> = HashMap::new();
47 let primary_field = &self.column_names[self.primary_column];
48 let mut escaped = false;
49 let mut in_field = false;
50 let mut field = None;
51 let mut text = String::new();
52 self.column_ranges.clear();
53 self.column_ranges
54 .push((0..usize::MAX, Some(primary_field.clone())));
55
56 macro_rules! finish_field {
57 () => {
58 let key = field.take().unwrap_or(primary_field);
59
60 let pat = text.strip_suffix(' ').unwrap_or(&text);
64
65 if let Some(pattern) = fields.get_mut(key) {
66 pattern.push(' ');
67 pattern.push_str(pat);
68 } else {
69 fields.insert(key.clone(), pat.to_string());
70 }
71 text.clear();
72 };
73 }
74
75 for (idx, ch) in input.char_indices() {
76 match ch {
77 _ if escaped => {
79 if ch != '%' {
83 text.push('\\');
84 }
85 text.push(ch);
86 escaped = false;
87 }
88 '\\' => escaped = !escaped,
89 '%' => {
90 if !text.is_empty() {
91 finish_field!();
92 }
93 let (range, _field) = self
94 .column_ranges
95 .last_mut()
96 .expect("column_ranges is non-empty");
97 range.end = idx;
98 in_field = true;
99 }
100 ' ' if in_field => {
101 text.clear();
102 in_field = false;
103 }
104 _ if in_field => {
105 text.push(ch);
106 field = self
109 .column_names
110 .iter()
111 .filter(|col| col.starts_with(&text))
112 .min_by_key(|col| col.len());
114
115 if let Some((_range, current_field)) = self
117 .column_ranges
118 .last_mut()
119 .filter(|(range, _)| range.end == usize::MAX)
120 {
121 *current_field = field.cloned();
122 } else {
123 self.column_ranges.push((idx..usize::MAX, field.cloned()));
124 }
125 }
126 _ => text.push(ch),
127 }
128 }
129
130 if !in_field && !text.is_empty() {
131 finish_field!();
132 }
133
134 let new_inner: HashMap<_, _> = fields
135 .into_iter()
136 .map(|(field, query)| (field, query.as_str().into()))
137 .collect();
138
139 mem::replace(&mut self.inner, new_inner)
140 }
141
142 pub fn active_column(&self, cursor: usize) -> Option<&Arc<str>> {
149 let point = self
150 .column_ranges
151 .partition_point(|(range, _field)| cursor > range.end);
152
153 self.column_ranges
154 .get(point)
155 .filter(|(range, _field)| cursor >= range.start && cursor <= range.end)
156 .and_then(|(_range, field)| field.as_ref())
157 }
158}