things3_cloud/store/
state.rs1use 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}