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