Skip to main content

things3_cloud/wire/
recurrence.rs

1use std::collections::BTreeMap;
2
3use num_enum::{FromPrimitive, IntoPrimitive};
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use strum::{Display, EnumString};
7
8/// Recurrence rule payload (`rr`) for recurring templates.
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
10pub struct RecurrenceRule {
11    /// `tp`: recurrence mode.
12    #[serde(rename = "tp", default)]
13    pub repeat_type: RecurrenceType,
14
15    /// `fu`: frequency unit bitmask.
16    #[serde(rename = "fu", default = "default_frequency_unit")]
17    pub frequency_unit: FrequencyUnit,
18
19    /// `fa`: frequency amount (every N units).
20    #[serde(rename = "fa", default = "default_frequency_amount")]
21    pub frequency_amount: i32,
22
23    /// `of`: offsets (weekday/day/ordinal selectors).
24    #[serde(rename = "of", default)]
25    pub offsets: Vec<BTreeMap<String, Value>>,
26
27    /// `sr`: recurrence start reference day timestamp.
28    #[serde(rename = "sr", default)]
29    pub start_reference: Option<i64>,
30
31    /// `ia`: initial anchor day timestamp for recurrence calculations.
32    #[serde(rename = "ia", default)]
33    pub initial_anchor: Option<i64>,
34
35    /// `ed`: recurrence end day timestamp (`64092211200` ~= effectively never).
36    #[serde(rename = "ed", default = "default_recurrence_end_date")]
37    pub end_date: i64,
38
39    /// `rc`: repeat count.
40    #[serde(rename = "rc", default)]
41    pub repeat_count: i32,
42
43    /// `ts`: task skip behavior metadata.
44    #[serde(rename = "ts", default)]
45    pub task_skip: i32,
46
47    /// `rrv`: recurrence rule version.
48    #[serde(rename = "rrv", default = "default_recurrence_rule_version")]
49    pub recurrence_rule_version: i32,
50}
51
52/// Recurrence mode (`rr.tp`).
53#[derive(
54    Debug,
55    Clone,
56    Copy,
57    Serialize,
58    Deserialize,
59    PartialEq,
60    Eq,
61    Display,
62    EnumString,
63    FromPrimitive,
64    IntoPrimitive,
65)]
66#[repr(i32)]
67#[serde(from = "i32", into = "i32")]
68pub enum RecurrenceType {
69    /// Fixed schedule cadence.
70    FixedSchedule = 0,
71    /// Interval anchored after completion date.
72    AfterCompletion = 1,
73
74    /// Unknown value preserved for forward compatibility.
75    #[num_enum(catch_all)]
76    #[strum(disabled, to_string = "{0}")]
77    Unknown(i32),
78}
79
80#[expect(
81    clippy::derivable_impls,
82    reason = "num_enum(catch_all) conflicts with #[default]"
83)]
84impl Default for RecurrenceType {
85    fn default() -> Self {
86        Self::FixedSchedule
87    }
88}
89
90/// Recurrence frequency unit (`rr.fu`).
91#[derive(
92    Debug,
93    Clone,
94    Copy,
95    Serialize,
96    Deserialize,
97    PartialEq,
98    Eq,
99    Display,
100    EnumString,
101    FromPrimitive,
102    IntoPrimitive,
103)]
104#[repr(i32)]
105#[serde(from = "i32", into = "i32")]
106pub enum FrequencyUnit {
107    /// Daily bitmask value `8`.
108    Daily = 8,
109    /// Monthly bitmask value `16`.
110    Monthly = 16,
111    /// Weekly bitmask value `256`.
112    Weekly = 256,
113
114    /// Unknown value preserved for forward compatibility.
115    #[num_enum(catch_all)]
116    #[strum(disabled, to_string = "{0}")]
117    Unknown(i32),
118}
119
120#[expect(
121    clippy::derivable_impls,
122    reason = "num_enum(catch_all) conflicts with #[default]"
123)]
124impl Default for FrequencyUnit {
125    fn default() -> Self {
126        Self::Weekly
127    }
128}
129
130/// Default recurrence frequency unit (`rr.fu`) is weekly.
131fn default_frequency_unit() -> FrequencyUnit {
132    FrequencyUnit::Weekly
133}
134
135/// Default recurrence frequency amount (`rr.fa`) is every 1 unit.
136const fn default_frequency_amount() -> i32 {
137    1
138}
139
140/// Default recurrence end date (`rr.ed`) far in the future (~year 4001).
141const fn default_recurrence_end_date() -> i64 {
142    64_092_211_200
143}
144
145/// Current observed recurrence rule version (`rrv`).
146const fn default_recurrence_rule_version() -> i32 {
147    4
148}