Skip to main content

things3_cloud/store/
state.rs

1use std::collections::HashMap;
2
3use crate::{
4    ids::ThingsId,
5    store::entities::{
6        AreaStateProps, ChecklistItemStateProps, StateObject, StateProperties, TagStateProps,
7        TaskStateProps,
8    },
9    wire::{
10        area::AreaPatch,
11        checklist::ChecklistItemPatch,
12        tags::TagPatch,
13        task::TaskPatch,
14        wire_object::{OperationType, Properties, WireItem, WireObject},
15    },
16};
17
18pub type RawState = HashMap<ThingsId, StateObject>;
19
20fn apply_task_patch(task: &mut TaskStateProps, patch: TaskPatch) {
21    if let Some(title) = patch.title {
22        task.title = title;
23    }
24    if let Some(notes) = patch.notes {
25        task.notes = notes.to_plain_text();
26    }
27    if let Some(start_location) = patch.start_location {
28        task.start_location = start_location;
29    }
30    if let Some(scheduled_date) = patch.scheduled_date {
31        task.scheduled_date = scheduled_date.map(|v| v as f64);
32    }
33    if let Some(today_index_reference) = patch.today_index_reference {
34        task.today_index_reference = today_index_reference;
35    }
36    if let Some(parent_project_ids) = patch.parent_project_ids {
37        task.parent_project_ids = parent_project_ids;
38    }
39    if let Some(area_ids) = patch.area_ids {
40        task.area_ids = area_ids;
41    }
42    if let Some(action_group_ids) = patch.action_group_ids {
43        task.action_group_ids = action_group_ids;
44    }
45    if let Some(tag_ids) = patch.tag_ids {
46        task.tag_ids = tag_ids;
47    }
48    if let Some(evening_bit) = patch.evening_bit {
49        task.evening_bit = evening_bit;
50    }
51    if let Some(modification_date) = patch.modification_date {
52        task.modification_date = Some(modification_date);
53    }
54
55    if let Some(item_type) = patch.item_type {
56        task.item_type = item_type;
57    }
58    if let Some(status) = patch.status {
59        task.status = status;
60    }
61    if let Some(stop_date) = patch.stop_date {
62        task.stop_date = stop_date;
63    }
64    if let Some(deadline) = patch.deadline {
65        task.deadline = deadline;
66    }
67    if let Some(sort_index) = patch.sort_index {
68        task.sort_index = sort_index;
69    }
70    if let Some(today_sort_index) = patch.today_sort_index {
71        task.today_sort_index = today_sort_index;
72    }
73    if let Some(recurrence_rule) = patch.recurrence_rule {
74        task.recurrence_rule = recurrence_rule;
75    }
76    if let Some(recurrence_template_ids) = patch.recurrence_template_ids {
77        task.recurrence_template_ids = recurrence_template_ids;
78    }
79    if let Some(instance_creation_paused) = patch.instance_creation_paused {
80        task.instance_creation_paused = instance_creation_paused;
81    }
82    if let Some(leaves_tombstone) = patch.leaves_tombstone {
83        task.leaves_tombstone = leaves_tombstone;
84    }
85    if let Some(trashed) = patch.trashed {
86        task.trashed = trashed;
87    }
88    if let Some(creation_date) = patch.creation_date {
89        task.creation_date = creation_date;
90    }
91}
92
93fn apply_checklist_patch(item: &mut ChecklistItemStateProps, patch: ChecklistItemPatch) {
94    if let Some(title) = patch.title {
95        item.title = title;
96    }
97    if let Some(status) = patch.status {
98        item.status = status;
99    }
100    if let Some(task_ids) = patch.task_ids {
101        item.task_ids = task_ids;
102    }
103    if let Some(sort_index) = patch.sort_index {
104        item.sort_index = sort_index;
105    }
106}
107
108fn apply_area_patch(area: &mut AreaStateProps, patch: AreaPatch) {
109    if let Some(title) = patch.title {
110        area.title = title;
111    }
112    if let Some(tag_ids) = patch.tag_ids {
113        area.tag_ids = tag_ids;
114    }
115    if let Some(sort_index) = patch.sort_index {
116        area.sort_index = sort_index;
117    }
118}
119
120fn apply_tag_patch(tag: &mut TagStateProps, patch: TagPatch) {
121    if let Some(title) = patch.title {
122        tag.title = title;
123    }
124    if let Some(parent_ids) = patch.parent_ids {
125        tag.parent_ids = parent_ids;
126    }
127    if let Some(shortcut) = patch.shortcut {
128        tag.shortcut = shortcut;
129    }
130    if let Some(sort_index) = patch.sort_index {
131        tag.sort_index = sort_index;
132    }
133}
134
135fn wire_object_properties(obj: &WireObject) -> StateProperties {
136    match obj.properties() {
137        Ok(payload) => payload.into(),
138        Err(_) => StateProperties::Other,
139    }
140}
141
142fn insert_state_object(state: &mut RawState, uuid: ThingsId, obj: WireObject) {
143    let properties = wire_object_properties(&obj);
144    state.insert(
145        uuid,
146        StateObject {
147            entity_type: obj.entity_type,
148            properties,
149        },
150    );
151}
152
153fn apply_update_payload(existing: &mut StateObject, payload: Properties) {
154    match (&mut existing.properties, payload) {
155        (StateProperties::Task(task), Properties::TaskUpdate(patch)) => {
156            apply_task_patch(task, patch);
157        }
158        (StateProperties::ChecklistItem(item), Properties::ChecklistUpdate(patch)) => {
159            apply_checklist_patch(item, patch);
160        }
161        (StateProperties::Area(area), Properties::AreaUpdate(patch)) => {
162            apply_area_patch(area, patch);
163        }
164        (StateProperties::Tag(tag), Properties::TagUpdate(patch)) => {
165            apply_tag_patch(tag, patch);
166        }
167        (_, payload) => {
168            existing.properties = payload.into();
169        }
170    }
171}
172
173pub fn fold_item(item: WireItem, state: &mut RawState) {
174    for (uuid, obj) in item {
175        let Ok(uuid) = uuid.parse::<ThingsId>() else {
176            continue;
177        };
178        match obj.operation_type {
179            OperationType::Create => {
180                insert_state_object(state, uuid, obj);
181            }
182            OperationType::Update => {
183                if let Some(existing) = state.get_mut(&uuid) {
184                    match obj.properties() {
185                        Ok(payload) => apply_update_payload(existing, payload),
186                        Err(_) => existing.properties = StateProperties::Other,
187                    }
188                    if obj.entity_type.is_some() {
189                        existing.entity_type = obj.entity_type.clone();
190                    }
191                } else {
192                    insert_state_object(state, uuid, obj);
193                }
194            }
195            OperationType::Delete => {
196                state.remove(&uuid);
197            }
198            OperationType::Unknown(_) => {}
199        }
200    }
201}
202
203pub fn fold_items(items: impl IntoIterator<Item = WireItem>) -> RawState {
204    let mut state = RawState::new();
205    for item in items {
206        fold_item(item, &mut state);
207    }
208    state
209}