zen_expression/functions/
deprecated.rs

1use crate::functions::arguments::Arguments;
2use crate::functions::defs::{FunctionSignature, StaticFunction};
3use crate::functions::registry::FunctionDefinition;
4use crate::vm::helpers::{date_time, date_time_end_of, date_time_start_of, time};
5use crate::Variable as V;
6use anyhow::{anyhow, Context};
7use chrono::{Datelike, NaiveDateTime, Timelike};
8use rust_decimal::prelude::ToPrimitive;
9use std::rc::Rc;
10use strum_macros::{Display, EnumIter, EnumString, IntoStaticStr};
11
12#[derive(Debug, PartialEq, Eq, Hash, Display, EnumString, EnumIter, IntoStaticStr, Clone, Copy)]
13#[strum(serialize_all = "camelCase")]
14pub enum DeprecatedFunction {
15    Date,
16    Time,
17    Duration,
18    Year,
19    DayOfWeek,
20    DayOfMonth,
21    DayOfYear,
22    WeekOfYear,
23    MonthOfYear,
24    MonthString,
25    DateString,
26    WeekdayString,
27    StartOf,
28    EndOf,
29}
30
31impl From<&DeprecatedFunction> for Rc<dyn FunctionDefinition> {
32    fn from(value: &DeprecatedFunction) -> Self {
33        use crate::variable::VariableType as VT;
34        use DeprecatedFunction as DF;
35
36        let s: Rc<dyn FunctionDefinition> = match value {
37            DF::Date => Rc::new(StaticFunction {
38                implementation: imp::parse_date,
39                signature: FunctionSignature::single(VT::Any, VT::Number),
40            }),
41
42            DF::Time => Rc::new(StaticFunction {
43                implementation: imp::parse_time,
44                signature: FunctionSignature::single(VT::Any, VT::Number),
45            }),
46
47            DF::Duration => Rc::new(StaticFunction {
48                implementation: imp::parse_duration,
49                signature: FunctionSignature::single(VT::Any, VT::Number),
50            }),
51
52            DF::Year => Rc::new(StaticFunction {
53                implementation: imp::year,
54                signature: FunctionSignature::single(VT::Any, VT::Number),
55            }),
56
57            DF::DayOfWeek => Rc::new(StaticFunction {
58                implementation: imp::day_of_week,
59                signature: FunctionSignature::single(VT::Any, VT::Number),
60            }),
61
62            DF::DayOfMonth => Rc::new(StaticFunction {
63                implementation: imp::day_of_month,
64                signature: FunctionSignature::single(VT::Any, VT::Number),
65            }),
66
67            DF::DayOfYear => Rc::new(StaticFunction {
68                implementation: imp::day_of_year,
69                signature: FunctionSignature::single(VT::Any, VT::Number),
70            }),
71
72            DF::WeekOfYear => Rc::new(StaticFunction {
73                implementation: imp::week_of_year,
74                signature: FunctionSignature::single(VT::Any, VT::Number),
75            }),
76
77            DF::MonthOfYear => Rc::new(StaticFunction {
78                implementation: imp::month_of_year,
79                signature: FunctionSignature::single(VT::Any, VT::Number),
80            }),
81
82            DF::MonthString => Rc::new(StaticFunction {
83                implementation: imp::month_string,
84                signature: FunctionSignature::single(VT::Any, VT::String),
85            }),
86
87            DF::DateString => Rc::new(StaticFunction {
88                implementation: imp::date_string,
89                signature: FunctionSignature::single(VT::Any, VT::String),
90            }),
91
92            DF::WeekdayString => Rc::new(StaticFunction {
93                implementation: imp::weekday_string,
94                signature: FunctionSignature::single(VT::Any, VT::String),
95            }),
96
97            DF::StartOf => Rc::new(StaticFunction {
98                implementation: imp::start_of,
99                signature: FunctionSignature {
100                    parameters: vec![VT::Any, VT::String],
101                    return_type: VT::Number,
102                },
103            }),
104
105            DF::EndOf => Rc::new(StaticFunction {
106                implementation: imp::end_of,
107                signature: FunctionSignature {
108                    parameters: vec![VT::Any, VT::String],
109                    return_type: VT::Number,
110                },
111            }),
112        };
113
114        s
115    }
116}
117
118mod imp {
119    use super::*;
120    use crate::vm::helpers::DateUnit;
121
122    fn __internal_convert_datetime(timestamp: &V) -> anyhow::Result<NaiveDateTime> {
123        timestamp
124            .try_into()
125            .context("Failed to convert value to date time")
126    }
127
128    pub fn parse_date(args: Arguments) -> anyhow::Result<V> {
129        let a = args.var(0)?;
130
131        let ts = match a {
132            V::String(a) => {
133                let dt = date_time(a.as_ref())?;
134                #[allow(deprecated)]
135                dt.timestamp()
136            }
137            V::Number(a) => a.to_i64().context("Number overflow")?,
138            _ => return Err(anyhow!("Unsupported type for date function")),
139        };
140
141        Ok(V::Number(ts.into()))
142    }
143
144    pub fn parse_time(args: Arguments) -> anyhow::Result<V> {
145        let a = args.var(0)?;
146
147        let ts = match a {
148            V::String(a) => time(a.as_ref())?.num_seconds_from_midnight(),
149            V::Number(a) => a.to_u32().context("Number overflow")?,
150            _ => return Err(anyhow!("Unsupported type for time function")),
151        };
152
153        Ok(V::Number(ts.into()))
154    }
155
156    pub fn parse_duration(args: Arguments) -> anyhow::Result<V> {
157        let a = args.var(0)?;
158
159        let dur = match a {
160            V::String(a) => humantime::parse_duration(a.as_ref())?.as_secs(),
161            V::Number(n) => n.to_u64().context("Number overflow")?,
162            _ => return Err(anyhow!("Unsupported type for duration function")),
163        };
164
165        Ok(V::Number(dur.into()))
166    }
167
168    pub fn year(args: Arguments) -> anyhow::Result<V> {
169        let timestamp = args.var(0)?;
170        let time = __internal_convert_datetime(&timestamp)?;
171        Ok(V::Number(time.year().into()))
172    }
173
174    pub fn day_of_week(args: Arguments) -> anyhow::Result<V> {
175        let timestamp = args.var(0)?;
176        let time = __internal_convert_datetime(&timestamp)?;
177        Ok(V::Number(time.weekday().number_from_monday().into()))
178    }
179
180    pub fn day_of_month(args: Arguments) -> anyhow::Result<V> {
181        let timestamp = args.var(0)?;
182        let time = __internal_convert_datetime(&timestamp)?;
183        Ok(V::Number(time.day().into()))
184    }
185
186    pub fn day_of_year(args: Arguments) -> anyhow::Result<V> {
187        let timestamp = args.var(0)?;
188        let time = __internal_convert_datetime(&timestamp)?;
189        Ok(V::Number(time.ordinal().into()))
190    }
191
192    pub fn week_of_year(args: Arguments) -> anyhow::Result<V> {
193        let timestamp = args.var(0)?;
194        let time = __internal_convert_datetime(&timestamp)?;
195        Ok(V::Number(time.iso_week().week().into()))
196    }
197
198    pub fn month_of_year(args: Arguments) -> anyhow::Result<V> {
199        let timestamp = args.var(0)?;
200        let time = __internal_convert_datetime(&timestamp)?;
201        Ok(V::Number(time.month().into()))
202    }
203
204    pub fn month_string(args: Arguments) -> anyhow::Result<V> {
205        let timestamp = args.var(0)?;
206        let time = __internal_convert_datetime(&timestamp)?;
207        Ok(V::String(Rc::from(time.format("%b").to_string())))
208    }
209
210    pub fn weekday_string(args: Arguments) -> anyhow::Result<V> {
211        let timestamp = args.var(0)?;
212        let time = __internal_convert_datetime(&timestamp)?;
213        Ok(V::String(Rc::from(time.weekday().to_string())))
214    }
215
216    pub fn date_string(args: Arguments) -> anyhow::Result<V> {
217        let timestamp = args.var(0)?;
218        let time = __internal_convert_datetime(&timestamp)?;
219        Ok(V::String(Rc::from(time.to_string())))
220    }
221
222    pub fn start_of(args: Arguments) -> anyhow::Result<V> {
223        let timestamp = args.var(0)?;
224        let unit_name = args.str(1)?;
225
226        let datetime = __internal_convert_datetime(&timestamp)?;
227        let unit = DateUnit::try_from(unit_name).context("Invalid date unit")?;
228
229        let result =
230            date_time_start_of(datetime, unit).context("Failed to calculate start of period")?;
231
232        #[allow(deprecated)]
233        Ok(V::Number(result.timestamp().into()))
234    }
235
236    pub fn end_of(args: Arguments) -> anyhow::Result<V> {
237        let timestamp = args.var(0)?;
238        let unit_name = args.str(1)?;
239
240        let datetime = __internal_convert_datetime(&timestamp)?;
241        let unit = DateUnit::try_from(unit_name).context("Invalid date unit")?;
242
243        let result =
244            date_time_end_of(datetime, unit).context("Failed to calculate end of period")?;
245
246        #[allow(deprecated)]
247        Ok(V::Number(result.timestamp().into()))
248    }
249}