synthphonia/text/formatting/
month.rs

1use std::collections::HashSet;
2
3use chrono::NaiveTime;
4use regex::Regex;
5
6use crate::forward::enumeration::Enumerator1;
7use crate::impl_op1_opt;
8use crate::utils::F64;
9use crate::value::{ConstValue, Value};
10use crate::{impl_name, impl_op1, parser::config::Config};
11use chrono::Timelike;
12
13use crate::galloc::{AllocForExactSizeIter, TryAllocForExactSizeIter};
14
15use super::FormattingOp;
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17pub struct FormatMonth(usize, Option<bool>);
18
19impl FormatMonth {
20    pub fn from_config(config: &Config) -> Self {
21        Self(
22            config.get_usize("cost").unwrap_or(1),
23            config.get_bool("abbv"),
24        )
25    }
26}
27
28impl FormatMonth {
29    pub fn name() -> &'static str {
30        "month.fmt"
31    }
32}
33
34impl std::fmt::Display for FormatMonth {
35    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36        if let Some(abbv) = self.1 {
37            write!(f, "month.fmt #abbv:{}", abbv)
38        } else {
39            write!(f, "month.fmt")
40        }
41    }
42}
43
44impl Default for FormatMonth {
45    fn default() -> Self {
46        Self::from_config(&Default::default())
47    }
48}
49
50impl Enumerator1 for FormatMonth {
51    fn enumerate(
52        &self,
53        this: &'static crate::expr::ops::Op1Enum,
54        exec: &'static crate::forward::executor::Executor,
55        opnt: [usize; 1],
56    ) -> Result<(), ()> {
57        Ok(())
58    }
59}
60
61impl crate::expr::ops::Op1 for FormatMonth {
62    fn cost(&self) -> usize {
63        self.0
64    }
65    fn try_eval(&self, a1: crate::value::Value) -> (bool, crate::value::Value) {
66        match a1 {
67            crate::value::Value::Int(s1) => {
68                let a = s1.iter().map(|&s1| {
69                    if !(1..=12).contains(&s1) { return ""; }
70                    let months_abbv = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
71                    let months_full = ["", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
72                    
73                    if let Some(true) = self.1 {
74                        months_abbv[s1 as usize]
75                    } else {
76                        months_full[s1 as usize]
77                    }
78                }).galloc_scollect();
79                (true, a.into())
80            }
81            _ => (false, Value::Null),
82        }
83    }
84}
85
86lazy_static::lazy_static! {
87    static ref REGEX: Regex = {
88        let month_literal = "(?<month>Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|(Nov|Dec)(?:ember)?)";
89        Regex::new(format!(r"^{month_literal}").as_str()).unwrap()
90    };
91}
92
93impl FormattingOp for FormatMonth {
94    fn format(
95        &self,
96        input: &'static str,
97    ) -> Option<(Self, crate::value::ConstValue, &'static str)> {
98        let months_arr = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
99        let months_abbv = HashSet::from(["Jan", "Feb", "Mar", "Apr", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]);
100        let months_full = HashSet::from(["January", "February", "March", "April", "June", "July", "August", "September", "October", "November", "December"]);
101        if let Some(caps) = REGEX.captures(input) {
102            if caps.name("month").is_some() {
103                let m = caps.name("month").unwrap().as_str();
104                let month = months_arr.iter().enumerate().find(|(_, s)| ***s == m[0..3]).unwrap().0 as u32 + 1;
105                let abbv = if months_abbv.contains(m) { Some(true) } else if months_full.contains(m) { Some(false) } else { None } ;
106                return Some((Self(1, abbv), month.into(), &input[caps.get(0).unwrap().as_str().len()..]));
107            }
108        }
109        None
110    }
111
112    fn union(self, other: Self) -> Option<Self> {
113        Some(Self(1, conflict(self.1, other.1)?))
114    }
115
116    fn bad_value() -> crate::value::ConstValue {
117        crate::value::ConstValue::Int(0.into())
118    }
119}
120
121fn conflict(a: Option<bool>, b: Option<bool>) -> Option<Option<bool>> {
122    match (a, b) {
123        (Some(x), Some(y)) if x != y => { None }
124        (Some(x), _) | (None, Some(x)) => { Some(Some(x)) }
125        (None, None) => { Some(None) }
126    }
127}