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    use crate::vm::VMError;
121    use zen_types::variable::Variable;
122
123    fn __internal_convert_datetime(timestamp: &V) -> anyhow::Result<NaiveDateTime> {
124        match timestamp {
125            Variable::String(a) => date_time(a),
126            #[allow(deprecated)]
127            Variable::Number(a) => NaiveDateTime::from_timestamp_opt(
128                a.to_i64().ok_or_else(|| VMError::OpcodeErr {
129                    opcode: "DateManipulation".into(),
130                    message: "Failed to extract date".into(),
131                })?,
132                0,
133            )
134            .ok_or_else(|| VMError::ParseDateTimeErr {
135                timestamp: a.to_string(),
136            }),
137            _ => Err(VMError::OpcodeErr {
138                opcode: "DateManipulation".into(),
139                message: "Unsupported type".into(),
140            }),
141        }
142        .context("Failed to convert value to date time")
143    }
144
145    pub fn parse_date(args: Arguments) -> anyhow::Result<V> {
146        let a = args.var(0)?;
147
148        let ts = match a {
149            V::String(a) => {
150                let dt = date_time(a.as_ref())?;
151                #[allow(deprecated)]
152                dt.timestamp()
153            }
154            V::Number(a) => a.to_i64().context("Number overflow")?,
155            _ => return Err(anyhow!("Unsupported type for date function")),
156        };
157
158        Ok(V::Number(ts.into()))
159    }
160
161    pub fn parse_time(args: Arguments) -> anyhow::Result<V> {
162        let a = args.var(0)?;
163
164        let ts = match a {
165            V::String(a) => time(a.as_ref())?.num_seconds_from_midnight(),
166            V::Number(a) => a.to_u32().context("Number overflow")?,
167            _ => return Err(anyhow!("Unsupported type for time function")),
168        };
169
170        Ok(V::Number(ts.into()))
171    }
172
173    pub fn parse_duration(args: Arguments) -> anyhow::Result<V> {
174        let a = args.var(0)?;
175
176        let dur = match a {
177            V::String(a) => humantime::parse_duration(a.as_ref())?.as_secs(),
178            V::Number(n) => n.to_u64().context("Number overflow")?,
179            _ => return Err(anyhow!("Unsupported type for duration function")),
180        };
181
182        Ok(V::Number(dur.into()))
183    }
184
185    pub fn year(args: Arguments) -> anyhow::Result<V> {
186        let timestamp = args.var(0)?;
187        let time = __internal_convert_datetime(&timestamp)?;
188        Ok(V::Number(time.year().into()))
189    }
190
191    pub fn day_of_week(args: Arguments) -> anyhow::Result<V> {
192        let timestamp = args.var(0)?;
193        let time = __internal_convert_datetime(&timestamp)?;
194        Ok(V::Number(time.weekday().number_from_monday().into()))
195    }
196
197    pub fn day_of_month(args: Arguments) -> anyhow::Result<V> {
198        let timestamp = args.var(0)?;
199        let time = __internal_convert_datetime(&timestamp)?;
200        Ok(V::Number(time.day().into()))
201    }
202
203    pub fn day_of_year(args: Arguments) -> anyhow::Result<V> {
204        let timestamp = args.var(0)?;
205        let time = __internal_convert_datetime(&timestamp)?;
206        Ok(V::Number(time.ordinal().into()))
207    }
208
209    pub fn week_of_year(args: Arguments) -> anyhow::Result<V> {
210        let timestamp = args.var(0)?;
211        let time = __internal_convert_datetime(&timestamp)?;
212        Ok(V::Number(time.iso_week().week().into()))
213    }
214
215    pub fn month_of_year(args: Arguments) -> anyhow::Result<V> {
216        let timestamp = args.var(0)?;
217        let time = __internal_convert_datetime(&timestamp)?;
218        Ok(V::Number(time.month().into()))
219    }
220
221    pub fn month_string(args: Arguments) -> anyhow::Result<V> {
222        let timestamp = args.var(0)?;
223        let time = __internal_convert_datetime(&timestamp)?;
224        Ok(V::String(Rc::from(time.format("%b").to_string())))
225    }
226
227    pub fn weekday_string(args: Arguments) -> anyhow::Result<V> {
228        let timestamp = args.var(0)?;
229        let time = __internal_convert_datetime(&timestamp)?;
230        Ok(V::String(Rc::from(time.weekday().to_string())))
231    }
232
233    pub fn date_string(args: Arguments) -> anyhow::Result<V> {
234        let timestamp = args.var(0)?;
235        let time = __internal_convert_datetime(&timestamp)?;
236        Ok(V::String(Rc::from(time.to_string())))
237    }
238
239    pub fn start_of(args: Arguments) -> anyhow::Result<V> {
240        let timestamp = args.var(0)?;
241        let unit_name = args.str(1)?;
242
243        let datetime = __internal_convert_datetime(&timestamp)?;
244        let unit = DateUnit::try_from(unit_name).context("Invalid date unit")?;
245
246        let result =
247            date_time_start_of(datetime, unit).context("Failed to calculate start of period")?;
248
249        #[allow(deprecated)]
250        Ok(V::Number(result.timestamp().into()))
251    }
252
253    pub fn end_of(args: Arguments) -> anyhow::Result<V> {
254        let timestamp = args.var(0)?;
255        let unit_name = args.str(1)?;
256
257        let datetime = __internal_convert_datetime(&timestamp)?;
258        let unit = DateUnit::try_from(unit_name).context("Invalid date unit")?;
259
260        let result =
261            date_time_end_of(datetime, unit).context("Failed to calculate end of period")?;
262
263        #[allow(deprecated)]
264        Ok(V::Number(result.timestamp().into()))
265    }
266}