chrono_node/common/
word_boundary.rs1use std::sync::Mutex;
6
7use crate::core::chrono::{Extracted, Parser};
8use crate::core::context::ParsingContext;
9use crate::core::regex::{JsRegex, RegexMatch};
10
11pub trait WordBoundaryParser: Send + Sync {
13 fn inner_pattern(&self, context: &ParsingContext) -> JsRegex;
15
16 fn inner_extract(&self, context: &ParsingContext, m: &mut RegexMatch) -> Extracted;
18
19 fn pattern_left_boundary(&self) -> &str {
21 r"(\W|^)"
22 }
23}
24
25pub struct WithWordBoundary<P> {
28 inner: P,
29 cache: Mutex<Option<(String, String, JsRegex)>>,
30}
31
32impl<P: WordBoundaryParser> WithWordBoundary<P> {
33 pub fn new(inner: P) -> WithWordBoundary<P> {
34 WithWordBoundary {
35 inner,
36 cache: Mutex::new(None),
37 }
38 }
39}
40
41impl<P: WordBoundaryParser> Parser for WithWordBoundary<P> {
42 fn pattern(&self, context: &ParsingContext) -> JsRegex {
43 let inner = self.inner.inner_pattern(context);
44 let mut cache = self.cache.lock().unwrap_or_else(|e| e.into_inner());
45 if let Some((source, flags, pattern)) = cache.as_ref() {
46 if source == inner.source() && flags == inner.flags() {
47 return pattern.clone();
48 }
49 }
50 let pattern = JsRegex::new(
51 &format!("{}{}", self.inner.pattern_left_boundary(), inner.source()),
52 inner.flags(),
53 );
54 *cache = Some((inner.source().to_string(), inner.flags().to_string(), pattern.clone()));
55 pattern
56 }
57
58 fn extract(&self, context: &ParsingContext, m: &mut RegexMatch) -> Extracted {
59 let header_len = m.get(1).map_or(0, str::len);
60 m.index += header_len;
61 if let Some(Some(full)) = m.groups.first_mut() {
62 *full = full[header_len..].to_string();
63 }
64 for i in 2..m.groups.len() {
66 m.groups[i - 1] = m.groups[i].clone();
67 }
68 self.inner.inner_extract(context, m)
69 }
70}