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> for FrequencyNewArgs {
33 fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
34 let ext: PyResult<(i32,)> = ob.extract();
35 if ext.is_ok() {
36 let (x,) = ext.unwrap();
37 return Ok(Self::CalDays(x));
38 }
39 let ext: PyResult<(i32, Calendar)> = ob.extract();
40 if ext.is_ok() {
41 let (x, y) = ext.unwrap();
42 return Ok(Self::BusDays(x, y));
43 }
44 let ext: PyResult<(i32, Option<RollDay>)> = ob.extract();
45 if ext.is_ok() {
46 let (x, y) = ext.unwrap();
47 Ok(Self::Months(x, y))
48 } else {
49 Ok(Self::Zero())
51 }
52 }
53}
54
55#[pymethods]
56impl Frequency {
57 #[pyo3(name = "next")]
68 fn next_py(&self, date: NaiveDateTime) -> NaiveDateTime {
69 self.next(&date)
70 }
71
72 #[pyo3(name = "periods_per_annum")]
78 fn periods_per_annum_py(&self) -> f64 {
79 self.periods_per_annum()
80 }
81
82 #[pyo3(name = "unext")]
94 fn unext_py(&self, udate: NaiveDateTime) -> PyResult<NaiveDateTime> {
95 self.try_unext(&udate)
96 }
97
98 #[pyo3(name = "previous")]
109 fn previous_py(&self, date: NaiveDateTime) -> NaiveDateTime {
110 self.previous(&date)
111 }
112
113 #[pyo3(name = "uprevious")]
125 fn uprevious_py(&self, udate: NaiveDateTime) -> PyResult<NaiveDateTime> {
126 self.try_uprevious(&udate)
127 }
128
129 #[pyo3(name = "uregular")]
144 fn uregular_py(
145 &self,
146 ueffective: NaiveDateTime,
147 utermination: NaiveDateTime,
148 ) -> PyResult<Vec<NaiveDateTime>> {
149 self.try_uregular(&ueffective, &utermination)
150 }
151
152 #[pyo3(name = "infer_ustub")]
175 fn infer_ustub_py(
176 &self,
177 ueffective: NaiveDateTime,
178 utermination: NaiveDateTime,
179 short: bool,
180 front: bool,
181 ) -> PyResult<Option<NaiveDateTime>> {
182 if front {
183 self.try_infer_ufront_stub(&ueffective, &utermination, short)
184 } else {
185 self.try_infer_uback_stub(&ueffective, &utermination, short)
186 }
187 }
188
189 #[pyo3(name = "is_stub")]
204 fn is_stub_py(&self, ustart: NaiveDateTime, uend: NaiveDateTime, front: bool) -> bool {
205 if front {
206 self.is_front_stub(&ustart, &uend)
207 } else {
208 self.is_back_stub(&ustart, &uend)
209 }
210 }
211
212 #[pyo3(name = "string")]
218 fn string_py(&self) -> PyResult<String> {
219 match self {
220 Frequency::Zero {} => Ok("Z".to_string()),
221 Frequency::CalDays { number: n } => Ok(format!("{n}D")),
222 Frequency::BusDays {
223 number: n,
224 calendar: _,
225 } => Ok(format!("{n}B")),
226 Frequency::Months { number: 1, roll: _ } => Ok(format!("M")),
227 Frequency::Months { number: 2, roll: _ } => Ok(format!("B")),
228 Frequency::Months { number: 3, roll: _ } => Ok(format!("Q")),
229 Frequency::Months { number: 4, roll: _ } => Ok(format!("T")),
230 Frequency::Months { number: 6, roll: _ } => Ok(format!("S")),
231 Frequency::Months {
232 number: 12,
233 roll: _,
234 } => Ok(format!("A")),
235 _ => Err(PyValueError::new_err(
236 "No recognisable string representation for Frequency.",
237 )),
238 }
239 }
240
241 fn __str__(&self) -> String {
242 match self {
243 Frequency::Zero {} => "Z".to_string(),
244 Frequency::CalDays { number: n } => format!("{n}D"),
245 Frequency::BusDays {
246 number: n,
247 calendar: _,
248 } => format!("{n}B"),
249 Frequency::Months { number: n, roll: r } => {
250 let x = match r {
251 Some(v) => v.__str__(),
252 None => "none".to_string(),
253 };
254 format!("{n}M (roll: {x})")
255 }
256 }
257 }
258
259 fn __getnewargs__(&self) -> FrequencyNewArgs {
260 match self {
261 Frequency::BusDays {
262 number: n,
263 calendar: c,
264 } => FrequencyNewArgs::BusDays(*n, c.clone()),
265 Frequency::CalDays { number: n } => FrequencyNewArgs::CalDays(*n),
266 Frequency::Months { number: n, roll: r } => FrequencyNewArgs::Months(*n, *r),
267 Frequency::Zero {} => FrequencyNewArgs::Zero(),
268 }
269 }
270
271 #[new]
272 fn new_py(args: FrequencyNewArgs) -> Frequency {
273 match args {
274 FrequencyNewArgs::BusDays(n, c) => Frequency::BusDays {
275 number: n,
276 calendar: c,
277 },
278 FrequencyNewArgs::CalDays(n) => Frequency::CalDays { number: n },
279 FrequencyNewArgs::Months(n, r) => Frequency::Months { number: n, roll: r },
280 FrequencyNewArgs::Zero() => Frequency::Zero {},
281 }
282 }
283
284 fn __repr__(&self) -> String {
285 match self {
286 Frequency::Zero {} => format!("<rl.Frequency.Zero at {:p}>", self),
287 Frequency::CalDays { number: n } => {
288 format!("<rl.Frequency.CalDays({}) at {:p}>", n, self)
289 }
290 Frequency::BusDays {
291 number: n,
292 calendar: _,
293 } => format!("<rl.Frequency.BusDays({}, ...) at {:p}>", n, self),
294 Frequency::Months { number: n, roll: r } => match r {
295 Some(val) => format!("<rl.Frequency.Months({}, {:?}) at {:p}>", n, val, self),
296 None => format!("<rl.Frequency.Months({}, None) at {:p}>", n, self),
297 },
298 }
299 }
300
301 #[pyo3(name = "to_json")]
307 fn to_json_py(&self) -> PyResult<String> {
308 match DeserializedObj::Frequency(self.clone()).to_json() {
309 Ok(v) => Ok(v),
310 Err(_) => Err(PyValueError::new_err(
311 "Failed to serialize `Frequency` to JSON.",
312 )),
313 }
314 }
315}