1use crate::json::{DeserializedObj, JSON};
2use crate::scheduling::calendars::Calendar;
3use crate::scheduling::frequency::{Frequency, RollDay, Scheduling};
4
5use chrono::prelude::*;
6use pyo3::exceptions::PyValueError;
7use pyo3::prelude::*;
8use pyo3::types::PyTuple;
9
10enum FrequencyNewArgs {
11 CalDays(i32),
12 BusDays(i32, Calendar),
13 Months(i32, Option<RollDay>),
14 Zero(),
15}
16
17impl<'py> IntoPyObject<'py> for FrequencyNewArgs {
18 type Target = PyTuple;
19 type Output = Bound<'py, Self::Target>;
20 type Error = std::convert::Infallible;
21
22 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
23 match self {
24 FrequencyNewArgs::CalDays(x) => Ok((x,).into_pyobject(py).unwrap()),
25 FrequencyNewArgs::BusDays(x, y) => Ok((x, y).into_pyobject(py).unwrap()),
26 FrequencyNewArgs::Months(x, y) => Ok((x, y).into_pyobject(py).unwrap()),
27 FrequencyNewArgs::Zero() => Ok(PyTuple::empty(py)),
28 }
29 }
30}
31
32impl<'py> FromPyObject<'py, 'py> for FrequencyNewArgs {
33 type Error = PyErr;
34
35 fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result<Self, Self::Error> {
36 let ext: PyResult<(i32,)> = obj.extract();
37 if ext.is_ok() {
38 let (x,) = ext.unwrap();
39 return Ok(Self::CalDays(x));
40 }
41 let ext: PyResult<(i32, Calendar)> = obj.extract();
42 if ext.is_ok() {
43 let (x, y) = ext.unwrap();
44 return Ok(Self::BusDays(x, y));
45 }
46 let ext: PyResult<(i32, Option<RollDay>)> = obj.extract();
47 if ext.is_ok() {
48 let (x, y) = ext.unwrap();
49 Ok(Self::Months(x, y))
50 } else {
51 Ok(Self::Zero())
53 }
54 }
55}
56
57#[pymethods]
58impl Frequency {
59 #[pyo3(name = "next")]
70 fn next_py(&self, date: NaiveDateTime) -> NaiveDateTime {
71 self.next(&date)
72 }
73
74 #[pyo3(name = "periods_per_annum")]
80 fn periods_per_annum_py(&self) -> f64 {
81 self.periods_per_annum()
82 }
83
84 #[pyo3(name = "unext")]
96 fn unext_py(&self, udate: NaiveDateTime) -> PyResult<NaiveDateTime> {
97 self.try_unext(&udate)
98 }
99
100 #[pyo3(name = "previous")]
111 fn previous_py(&self, date: NaiveDateTime) -> NaiveDateTime {
112 self.previous(&date)
113 }
114
115 #[pyo3(name = "uprevious")]
127 fn uprevious_py(&self, udate: NaiveDateTime) -> PyResult<NaiveDateTime> {
128 self.try_uprevious(&udate)
129 }
130
131 #[pyo3(name = "uregular")]
146 fn uregular_py(
147 &self,
148 ueffective: NaiveDateTime,
149 utermination: NaiveDateTime,
150 ) -> PyResult<Vec<NaiveDateTime>> {
151 self.try_uregular(&ueffective, &utermination)
152 }
153
154 #[pyo3(name = "is_uregular")]
169 fn is_uregular_py(&self, ueffective: NaiveDateTime, utermination: NaiveDateTime) -> bool {
170 match self.try_uregular(&ueffective, &utermination) {
171 Err(_) => false,
172 Ok(_) => true,
173 }
174 }
175
176 #[pyo3(name = "infer_ustub")]
199 fn infer_ustub_py(
200 &self,
201 ueffective: NaiveDateTime,
202 utermination: NaiveDateTime,
203 short: bool,
204 front: bool,
205 ) -> PyResult<Option<NaiveDateTime>> {
206 if front {
207 self.try_infer_ufront_stub(&ueffective, &utermination, short)
208 } else {
209 self.try_infer_uback_stub(&ueffective, &utermination, short)
210 }
211 }
212
213 #[pyo3(name = "is_stub")]
228 fn is_stub_py(&self, ustart: NaiveDateTime, uend: NaiveDateTime, front: bool) -> bool {
229 if front {
230 self.is_front_stub(&ustart, &uend)
231 } else {
232 self.is_back_stub(&ustart, &uend)
233 }
234 }
235
236 #[pyo3(name = "string")]
242 fn string_py(&self) -> PyResult<String> {
243 match self {
244 Frequency::Zero {} => Ok("Z".to_string()),
245 Frequency::CalDays { number: n } => Ok(format!("{n}D")),
246 Frequency::BusDays {
247 number: n,
248 calendar: _,
249 } => Ok(format!("{n}B")),
250 Frequency::Months { number: 1, roll: _ } => Ok(format!("M")),
251 Frequency::Months { number: 2, roll: _ } => Ok(format!("B")),
252 Frequency::Months { number: 3, roll: _ } => Ok(format!("Q")),
253 Frequency::Months { number: 4, roll: _ } => Ok(format!("T")),
254 Frequency::Months { number: 6, roll: _ } => Ok(format!("S")),
255 Frequency::Months {
256 number: 12,
257 roll: _,
258 } => Ok(format!("A")),
259 _ => Err(PyValueError::new_err(
260 "No recognisable string representation for Frequency.",
261 )),
262 }
263 }
264
265 fn __str__(&self) -> String {
266 match self {
267 Frequency::Zero {} => "Z".to_string(),
268 Frequency::CalDays { number: n } => format!("{n}D"),
269 Frequency::BusDays {
270 number: n,
271 calendar: _,
272 } => format!("{n}B"),
273 Frequency::Months { number: n, roll: r } => {
274 let x = match r {
275 Some(v) => v.__str__(),
276 None => "none".to_string(),
277 };
278 format!("{n}M (roll: {x})")
279 }
280 }
281 }
282
283 fn __getnewargs__(&self) -> FrequencyNewArgs {
284 match self {
285 Frequency::BusDays {
286 number: n,
287 calendar: c,
288 } => FrequencyNewArgs::BusDays(*n, c.clone()),
289 Frequency::CalDays { number: n } => FrequencyNewArgs::CalDays(*n),
290 Frequency::Months { number: n, roll: r } => FrequencyNewArgs::Months(*n, *r),
291 Frequency::Zero {} => FrequencyNewArgs::Zero(),
292 }
293 }
294
295 #[new]
296 fn new_py(args: FrequencyNewArgs) -> Frequency {
297 match args {
298 FrequencyNewArgs::BusDays(n, c) => Frequency::BusDays {
299 number: n,
300 calendar: c,
301 },
302 FrequencyNewArgs::CalDays(n) => Frequency::CalDays { number: n },
303 FrequencyNewArgs::Months(n, r) => Frequency::Months { number: n, roll: r },
304 FrequencyNewArgs::Zero() => Frequency::Zero {},
305 }
306 }
307
308 fn __repr__(&self) -> String {
309 match self {
310 Frequency::Zero {} => format!("<rl.Frequency.Zero at {:p}>", self),
311 Frequency::CalDays { number: n } => {
312 format!("<rl.Frequency.CalDays({}) at {:p}>", n, self)
313 }
314 Frequency::BusDays {
315 number: n,
316 calendar: _,
317 } => format!("<rl.Frequency.BusDays({}, ...) at {:p}>", n, self),
318 Frequency::Months { number: n, roll: r } => match r {
319 Some(val) => format!("<rl.Frequency.Months({}, {:?}) at {:p}>", n, val, self),
320 None => format!("<rl.Frequency.Months({}, None) at {:p}>", n, self),
321 },
322 }
323 }
324
325 #[pyo3(name = "to_json")]
331 fn to_json_py(&self) -> PyResult<String> {
332 match DeserializedObj::Frequency(self.clone()).to_json() {
333 Ok(v) => Ok(v),
334 Err(_) => Err(PyValueError::new_err(
335 "Failed to serialize `Frequency` to JSON.",
336 )),
337 }
338 }
339}