visdom/mesdoc/selector/
mod.rs

1pub mod pattern;
2pub mod rule;
3use self::{pattern::BoxDynPattern, rule::Matcher};
4use crate::mesdoc::{constants::NAME_SELECTOR_ALL, error::Error};
5use lazy_static::lazy_static;
6pub use pattern::MatchedQueue;
7use pattern::{exec, Matched};
8use rule::{Rule, RULES};
9use std::{
10	str::FromStr,
11	sync::{Arc, Mutex},
12};
13
14lazy_static! {
15	static ref SPLITTER: Mutex<Vec<BoxDynPattern>> =
16		Mutex::new(Rule::get_queues(r##"{regexp#(\s*[>,~+]\s*|\s+)#}"##));
17	static ref ALL_RULE: Mutex<Option<Arc<Rule>>> = Mutex::new(None);
18}
19#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
20pub enum Combinator {
21	// descendants
22	ChildrenAll,
23	// children
24	Children,
25	// reverse for child
26	Parent,
27	// reverse for childrens
28	ParentAll,
29	// next all siblings
30	NextAll,
31	// next sibling
32	Next,
33	// reverse for next siblings
34	PrevAll,
35	// reverse for next sibling
36	Prev,
37	// siblings
38	Siblings,
39	// chain selectors
40	Chain,
41}
42
43// change string to combinator
44impl From<&str> for Combinator {
45	fn from(comb: &str) -> Self {
46		use Combinator::*;
47		match comb {
48			"" => ChildrenAll,
49			">" => Children,
50			"~" => NextAll,
51			"+" => Next,
52			_ => panic!("Not supported combinator string '{}'", comb),
53		}
54	}
55}
56
57impl Combinator {
58	pub fn reverse(&self) -> Self {
59		use Combinator::*;
60		match self {
61			ChildrenAll => ParentAll,
62			Children => Parent,
63			NextAll => PrevAll,
64			Next => Prev,
65			Chain => Chain,
66			_ => panic!("Not supported combinator reverse for '{:?}'", self),
67		}
68	}
69}
70
71pub type SelectorSegment = (Matcher, Combinator);
72#[derive(Default, Debug)]
73pub struct QueryProcess {
74	pub should_in: Option<SelectorGroupsItem>,
75	pub query: SelectorGroupsItem,
76}
77
78#[derive(Default, Debug)]
79pub struct Selector {
80	pub process: Vec<QueryProcess>,
81}
82
83type SelectorGroupsItem = Vec<Vec<SelectorSegment>>;
84type SelectorGroups = Vec<SelectorGroupsItem>;
85impl Selector {
86	pub fn new() -> Self {
87		Selector {
88			process: Vec::with_capacity(1),
89		}
90	}
91	pub fn from_str(context: &str, use_lookup: bool) -> Result<Self, Error> {
92		let chars: Vec<char> = context.chars().collect();
93		let total_len = chars.len();
94		let mut selector = Selector::new();
95		if total_len > 0 {
96			let mut index: usize = 0;
97			let mut comb = Combinator::ChildrenAll;
98			let mut prev_in = PrevInSelector::Begin;
99			let mut last_in = prev_in;
100			let mut groups: SelectorGroups = Vec::new();
101			let splitter = SPLITTER.lock().unwrap();
102			let rules = RULES.lock().unwrap();
103			Selector::add_group(&mut groups);
104			while index < total_len {
105				let next_chars = &chars[index..];
106				// first check if combinator
107				if let Some((matched, len, _)) = Rule::exec_queues(&splitter, next_chars) {
108					let op = matched[0].chars.iter().collect::<String>();
109					let op = op.trim();
110					if prev_in == PrevInSelector::Splitter {
111						// wrong multiple combinator
112						return Err(Error::InvalidSelector {
113							context: String::from(context),
114							reason: format!(
115								"Wrong combinator '{}' at index {}",
116								matched[0].chars.iter().collect::<String>(),
117								index
118							),
119						});
120					}
121					// find the match
122					index += len;
123					// set combinator
124					if op == "," {
125						if prev_in != PrevInSelector::Selector {
126							return Err(Error::InvalidSelector {
127								context: String::from(context),
128								reason: format!("Wrong empty selector before ',' at index  {}", index),
129							});
130						}
131						Selector::add_group(&mut groups);
132						comb = Combinator::ChildrenAll;
133					} else {
134						comb = Combinator::from(op);
135					}
136					// set prev is splitter
137					if op.is_empty() {
138						last_in = prev_in;
139						prev_in = PrevInSelector::Splitter;
140					} else {
141						prev_in = PrevInSelector::Splitter;
142						last_in = prev_in;
143					}
144					continue;
145				}
146				// then it must match a selector rule
147				let mut is_new_item = true;
148				if prev_in == PrevInSelector::Selector {
149					comb = Combinator::Chain;
150					is_new_item = false;
151				} else {
152					prev_in = PrevInSelector::Selector;
153					last_in = prev_in;
154				}
155				let mut finded = false;
156				for (_, r) in rules.iter() {
157					if let Some((mut matched, len, queue_num)) = r.exec(next_chars) {
158						// find the rule
159						index += len;
160						let queues = &r.queues;
161						if queue_num == queues.len() {
162							// push to selector
163							Selector::add_group_item(&mut groups, (r.make(matched), comb), is_new_item);
164							finded = true;
165						} else if queues[queue_num].is_nested() {
166							// nested selector
167							let (len, nested_matched) = Selector::parse_until(
168								&chars[index..],
169								&queues[queue_num + 1..],
170								&rules,
171								&splitter,
172								0,
173							)?;
174							index += len;
175							matched.extend(nested_matched);
176							Selector::add_group_item(&mut groups, (r.make(matched), comb), is_new_item);
177							finded = true;
178						}
179						break;
180					}
181				}
182				if !finded {
183					// no splitter, no selector rule
184					return Err(Error::InvalidSelector {
185						context: String::from(context),
186						reason: format!(
187							"Unrecognized selector '{}' at index {}",
188							next_chars.iter().collect::<String>(),
189							index
190						),
191					});
192				}
193			}
194			if last_in != PrevInSelector::Selector {
195				return Err(Error::InvalidSelector {
196					context: String::from(context),
197					reason: String::from("Wrong selector rule at last"),
198				});
199			}
200			// optimize groups to query process
201			selector.optimize(groups, use_lookup);
202		}
203		Ok(selector)
204	}
205	// add a selector group, splitted by ','
206	fn add_group(groups: &mut SelectorGroups) {
207		groups.push(Vec::with_capacity(2));
208	}
209	// add a selector group item
210	fn add_group_item(groups: &mut SelectorGroups, item: SelectorSegment, is_new: bool) {
211		if let Some(last_group) = groups.last_mut() {
212			if is_new {
213				last_group.push(vec![item]);
214			} else if let Some(last) = last_group.last_mut() {
215				last.push(item);
216			}
217		}
218	}
219	// optimize the parse process
220	fn optimize(&mut self, groups: SelectorGroups, use_lookup: bool) {
221		let mut process: Vec<QueryProcess> = Vec::with_capacity(groups.len());
222		for mut group in groups {
223			// first optimize the chain selectors, the rule who's priority is bigger will apply first
224			let mut max_index: usize = 0;
225			let mut max_priority: u32 = 0;
226			for (index, r) in group.iter_mut().enumerate() {
227				let mut total_priority = 0;
228				if r.len() > 1 {
229					let chain_comb = r[0].1;
230					r.sort_by(|a, b| b.0.priority.partial_cmp(&a.0.priority).unwrap());
231					let now_first = &mut r[0];
232					if now_first.1 != chain_comb {
233						now_first.1 = chain_comb;
234						total_priority += now_first.0.priority;
235						for n in &mut r[1..] {
236							n.1 = Combinator::Chain;
237							total_priority += n.0.priority;
238						}
239					}
240				}
241				if use_lookup {
242					total_priority = r.iter().map(|p| p.0.priority).sum();
243					if total_priority > max_priority {
244						max_priority = total_priority;
245						max_index = index;
246					}
247				}
248			}
249			// if the first combinator is child, and the max_index > 1, use the max_index's rule first
250			if use_lookup && max_index > 0 {
251				let is_child = matches!(
252					group[0][0].1,
253					Combinator::Children | Combinator::ChildrenAll
254				);
255				if is_child {
256					let query = group.split_off(max_index);
257					let should_in = Some(group);
258					process.push(QueryProcess { should_in, query });
259					continue;
260				}
261			}
262			process.push(QueryProcess {
263				should_in: None,
264				query: group,
265			});
266		}
267		self.process = process;
268	}
269	// change the combinator
270	pub fn head_combinator(&mut self, comb: Combinator) {
271		for p in &mut self.process {
272			let v = if let Some(should_in) = &mut p.should_in {
273				should_in
274			} else {
275				&mut p.query
276			};
277			if let Some(rule) = v.get_mut(0) {
278				let first_comb = rule[0].1;
279				match first_comb {
280					Combinator::ChildrenAll => rule[0].1 = comb,
281					_ => {
282						let segment = Selector::make_comb_all(comb);
283						v.insert(0, vec![segment]);
284					}
285				};
286			}
287		}
288	}
289	// make '*' with combinator
290	pub fn make_comb_all(comb: Combinator) -> SelectorSegment {
291		let mut all_rule = ALL_RULE.lock().unwrap();
292		if all_rule.is_none() {
293			let rules = RULES.lock().unwrap();
294			for (name, rule) in &rules[..] {
295				if *name == NAME_SELECTOR_ALL {
296					*all_rule = Some(Arc::clone(rule));
297					break;
298				}
299			}
300		}
301		let cur_rule = Arc::clone(all_rule.as_ref().expect("All rule must add to rules"));
302		let matcher = cur_rule.make(vec![]);
303		(matcher, comb)
304	}
305	// build a selector from a segment
306	pub fn from_segment(segment: SelectorSegment) -> Self {
307		let process = QueryProcess {
308			query: vec![vec![segment]],
309			should_in: None,
310		};
311		Selector {
312			process: vec![process],
313		}
314	}
315	// parse until
316	pub fn parse_until(
317		chars: &[char],
318		until: &[BoxDynPattern],
319		rules: &[(&str, Arc<Rule>)],
320		splitter: &[BoxDynPattern],
321		level: usize,
322	) -> Result<(usize, MatchedQueue), Error> {
323		let mut index = 0;
324		let total = chars.len();
325		let mut matched: MatchedQueue = Vec::with_capacity(until.len() + 1);
326		while index < total {
327			let next_chars = &chars[index..];
328			if let Some((_, len, _)) = Rule::exec_queues(splitter, next_chars) {
329				index += len;
330				continue;
331			}
332			let mut finded = false;
333			for (_, r) in rules.iter() {
334				if let Some((_, len, queue_num)) = r.exec(next_chars) {
335					let queues = &r.queues;
336					// find the rule
337					index += len;
338					if queue_num == queues.len() {
339						// push to selector
340						finded = true;
341					} else {
342						let (nest_count, _) = Selector::parse_until(
343							&chars[index..],
344							&queues[queue_num + 1..],
345							rules,
346							splitter,
347							level + 1,
348						)?;
349						index += nest_count;
350					}
351					break;
352				}
353			}
354			if !finded {
355				if level == 0 {
356					matched.push(Matched {
357						chars: chars[0..index].to_vec(),
358						name: "selector",
359						..Default::default()
360					});
361				}
362				if !until.is_empty() {
363					let (util_matched, count, queue_num, _) = exec(until, &chars[index..]);
364					if queue_num != until.len() {
365						let context = chars[index..].iter().collect::<String>();
366						return Err(Error::InvalidSelector {
367							context,
368							reason: format!("Nested selector parse error at index {}", index),
369						});
370					} else {
371						index += count;
372						if level == 0 {
373							matched.extend(util_matched);
374						}
375					}
376				}
377				break;
378			}
379		}
380		Ok((index, matched))
381	}
382}
383
384#[derive(PartialEq, Eq, Clone, Copy, Debug)]
385enum PrevInSelector {
386	Begin,
387	Splitter,
388	Selector,
389}
390
391impl FromStr for Selector {
392	type Err = Error;
393	fn from_str(selector: &str) -> Result<Self, Self::Err> {
394		Selector::from_str(selector, true)
395	}
396}
397
398#[cfg(test)]
399mod tests {
400	use super::{Combinator, QueryProcess, Selector};
401	#[test]
402	fn test_default() {
403		let def_selector = Selector::default();
404		assert!(def_selector.process.is_empty());
405		let def_process = QueryProcess::default();
406		assert!(def_process.should_in.is_none());
407		assert!(def_process.query.is_empty());
408	}
409	#[test]
410	fn test_combinator() {
411		let comb: Combinator = ">".into();
412		assert_eq!(comb, Combinator::Children);
413		assert_eq!(comb.reverse(), Combinator::Parent);
414	}
415
416	#[test]
417	fn test_combinator_reverse() {
418		assert_eq!(Combinator::Chain.reverse(), Combinator::Chain);
419	}
420
421	#[test]
422	#[should_panic]
423	fn test_combinator_unable_reverse() {
424		let _ = Combinator::Parent.reverse();
425	}
426
427	#[test]
428	#[should_panic]
429	fn test_wrong_combinator_string() {
430		let _: Combinator = "<".into();
431	}
432}