Skip to main content

things3_cloud/store/
state.rs

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