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