zen_expression/functions/
deprecated.rs

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