synthphonia/text/formatting/
month.rs1use 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}