chrono_node/core/utils/
pattern.rs1pub fn js_len(s: &str) -> usize {
5 s.encode_utf16().count()
6}
7
8pub fn repeated_timeunit_pattern(
10 prefix: &str,
11 single_timeunit_pattern: &str,
12 connector_pattern: Option<&str>,
13) -> String {
14 let connector = connector_pattern.unwrap_or(r"\s{0,5},?\s{0,5}");
15 let chars: Vec<char> = single_timeunit_pattern.chars().collect();
17 let mut no_capture = String::new();
18 for (i, &c) in chars.iter().enumerate() {
19 if c == '(' && chars.get(i + 1) != Some(&'?') {
20 no_capture.push_str("(?:");
21 } else {
22 no_capture.push(c);
23 }
24 }
25 format!("{prefix}{no_capture}(?:{connector}{no_capture}){{0,10}}")
26}
27
28pub fn match_any_pattern<'a>(terms: impl IntoIterator<Item = &'a str>) -> String {
31 let mut terms: Vec<&str> = terms.into_iter().collect();
32 terms.sort_by_key(|t| std::cmp::Reverse(js_len(t)));
33 format!("(?:{})", terms.join("|").replace('.', r"\."))
34}
35
36pub type Dictionary<V> = [(&'static str, V)];
38
39pub fn dict_get<V: Copy>(dictionary: &Dictionary<V>, key: &str) -> Option<V> {
41 dictionary.iter().find(|(k, _)| *k == key).map(|(_, v)| *v)
42}
43
44pub fn dict_keys<V>(dictionary: &Dictionary<V>) -> impl Iterator<Item = &'static str> + '_ {
46 dictionary.iter().map(|(k, _)| *k)
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52
53 #[test]
54 fn any_pattern_sorts_longest_first_stably() {
55 assert_eq!(
56 match_any_pattern(["mon", "monday", "tue", "sept."]),
57 r"(?:monday|sept\.|mon|tue)"
58 );
59 }
60
61 #[test]
62 fn repeated_pattern() {
63 assert_eq!(
64 repeated_timeunit_pattern("", "(a)(?:b)", Some(",")),
65 "(?:a)(?:b)(?:,(?:a)(?:b)){0,10}"
66 );
67 }
68
69 #[test]
70 fn js_length_counts_utf16() {
71 assert_eq!(js_len("a😀"), 3);
72 }
73}