1use anyhow::Result;
2use serde_json::Value;
3use sqlx::SqliteConnection;
4
5use crate::db::insert_change;
6use crate::workspaces::Workspace;
7
8pub mod op_type {
9 macro_rules! operations {
10 ($($name:ident = $value:literal;)*) => {
11 $(pub const $name: &str = $value;)*
12
13 pub const ALL: &[&str] = &[$($name),*];
15 };
16 }
17
18 operations! {
19 CREATE_TASK = "create_task";
20 MOVE_TASKS = "move_tasks";
21 SET_FIELD = "set_field";
22 RESOLVE_FIELD = "resolve_field";
23 LABEL_ADD = "label_add";
24 LABEL_REMOVE = "label_remove";
25 NOTE_ADD = "note_add";
26 NOTE_EDIT = "note_edit";
27 NOTE_DELETE = "note_delete";
28 DEPENDENCY_ADD = "dependency_add";
29 DEPENDENCY_REMOVE = "dependency_remove";
30 RELATED_ADD = "related_add";
31 RELATED_REMOVE = "related_remove";
32 EPIC_LINK_ADD = "epic_link_add";
33 EPIC_LINK_REMOVE = "epic_link_remove";
34 ATTACHMENT_ADD = "attachment_add";
35 ATTACHMENT_DELETE = "attachment_delete";
36 CREATE_PROJECT = "create_project";
37 SET_PROJECT_METADATA = "set_project_metadata";
38 PROJECT_DELETE = "project_delete";
39 CREATE_LABEL = "create_label";
40 SET_LABEL_NAME = "set_label_name";
41 LABEL_DELETE = "label_delete";
42 LABEL_RESTORE = "label_restore";
43 CREATE_WORKSPACE = "create_workspace";
44 SET_WORKSPACE_FIELD = "set_workspace_field";
45 CREATE_RECURRENCE_SERIES = "create_recurrence_series";
46 UPDATE_RECURRENCE_TEMPLATE = "update_recurrence_template";
47 PROJECT_RECURRENCE_OCCURRENCE = "project_recurrence_occurrence";
48 RESOLVE_RECURRENCE_OCCURRENCE = "resolve_recurrence_occurrence";
49 SET_RECURRENCE_STATE = "set_recurrence_state";
50 CREATE_METADATA_FIELD = "create_metadata_field";
51 SET_METADATA_FIELD = "set_metadata_field";
52 SET_TASK_METADATA = "set_task_metadata";
53 REMOVE_TASK_METADATA = "remove_task_metadata";
54 SET_RECURRENCE_METADATA = "set_recurrence_metadata";
55 REMOVE_RECURRENCE_METADATA = "remove_recurrence_metadata";
56 OPEN_RECURRENCE_PAUSE = "open_recurrence_pause";
57 CLOSE_RECURRENCE_PAUSE = "close_recurrence_pause";
58 STOP_RECURRENCE_SERIES = "stop_recurrence_series";
59 PUBLISH_DEVICE_LABEL = "publish_device_label";
60 DELETE_WORKSPACE = "delete_workspace";
61 }
62}
63
64pub enum ChangeEntity {
65 Task,
66 TaskMove,
67 Project,
68 Label,
69 MetadataField,
70 #[allow(dead_code)]
71 Workspace,
72 RecurrenceSeries,
73}
74
75impl ChangeEntity {
76 pub const fn as_str(&self) -> &'static str {
77 match self {
78 Self::Task => "task",
79 Self::TaskMove => "task_move",
80 Self::Project => "project",
81 Self::Label => "label",
82 Self::MetadataField => "metadata_field",
83 Self::Workspace => "workspace",
84 Self::RecurrenceSeries => "recurrence_series",
85 }
86 }
87}
88
89pub struct ChangePayload {
102 map: serde_json::Map<String, serde_json::Value>,
103}
104
105impl ChangePayload {
106 pub fn workspace(workspace: &Workspace) -> Self {
107 let mut map = serde_json::Map::new();
108 map.insert(
109 "workspace_id".to_string(),
110 Value::String(workspace.id.to_string()),
111 );
112 map.insert(
113 "workspace_key".to_string(),
114 Value::String(workspace.key.clone()),
115 );
116 Self { map }
117 }
118
119 pub fn set(mut self, key: &str, value: impl serde::Serialize) -> Self {
120 self.map.insert(
121 key.to_string(),
122 serde_json::to_value(value).expect("change payload value serialization"),
123 );
124 self
125 }
126
127 pub fn into_value(self) -> Value {
128 Value::Object(self.map)
129 }
130}
131
132pub(crate) async fn append_change(
138 conn: &mut SqliteConnection,
139 entity: ChangeEntity,
140 entity_id: &str,
141 field: Option<&str>,
142 op_type: &'static str,
143 payload: ChangePayload,
144) -> Result<String> {
145 insert_change(
146 conn,
147 entity.as_str(),
148 entity_id,
149 field,
150 op_type,
151 payload.into_value(),
152 None,
153 )
154 .await
155}