zen_expression/functions/
deprecated.rs1use 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(×tamp)?;
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(×tamp)?;
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(×tamp)?;
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(×tamp)?;
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(×tamp)?;
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(×tamp)?;
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(×tamp)?;
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(×tamp)?;
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(×tamp)?;
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(×tamp)?;
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(×tamp)?;
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}