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