1use std::fmt::{Display, Error, Formatter};
2use std::sync::Arc;
3
4use regex::Regex;
5
6use crate::engine::util::regex_match;
7use crate::item::RankBuilder;
8use crate::{CaseMatching, MatchEngine};
9use crate::{MatchRange, MatchResult, SkimItem};
10use std::cmp::min;
11
12#[derive(Debug)]
15pub struct RegexEngine {
16 query_regex: Option<Regex>,
17 rank_builder: Arc<RankBuilder>,
18}
19
20impl RegexEngine {
21 pub fn builder(query: &str, case: CaseMatching) -> Self {
22 let mut query_builder = String::new();
23
24 match case {
25 CaseMatching::Respect => {}
26 CaseMatching::Ignore => query_builder.push_str("(?i)"),
27 CaseMatching::Smart => {}
28 }
29
30 query_builder.push_str(query);
31
32 RegexEngine {
33 query_regex: Regex::new(&query_builder).ok(),
34 rank_builder: Default::default(),
35 }
36 }
37
38 pub fn rank_builder(mut self, rank_builder: Arc<RankBuilder>) -> Self {
39 self.rank_builder = rank_builder;
40 self
41 }
42
43 pub fn build(self) -> Self {
44 self
45 }
46}
47
48impl MatchEngine for RegexEngine {
49 fn match_item(&self, item: Arc<dyn SkimItem>) -> Option<MatchResult> {
50 let mut matched_result = None;
51 let item_text = item.text();
52 let default_range = [(0, item_text.len())];
53 for &(start, end) in item.get_matching_ranges().unwrap_or(&default_range) {
54 let start = min(start, item_text.len());
55 let end = min(end, item_text.len());
56 if self.query_regex.is_none() {
57 matched_result = Some((0, 0));
58 break;
59 }
60
61 matched_result =
62 regex_match(&item_text[start..end], &self.query_regex).map(|(s, e)| (s + start, e + start));
63
64 if matched_result.is_some() {
65 break;
66 }
67 }
68
69 let (begin, end) = matched_result?;
70 let score = (end - begin) as i32;
71 let item_len = item_text.len();
72
73 Some(MatchResult {
74 rank: self.rank_builder.build_rank(score, begin, end, item_len),
75 matched_range: MatchRange::ByteRange(begin, end),
76 })
77 }
78}
79
80impl Display for RegexEngine {
81 fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
82 write!(
83 f,
84 "(Regex: {})",
85 self.query_regex
86 .as_ref()
87 .map_or("".to_string(), |re| re.as_str().to_string())
88 )
89 }
90}