Skip to main content

boarddown_schema/
crdt.rs

1use serde::{Deserialize, Serialize};
2use ts_rs::TS;
3
4use crate::{TaskId, Status, Task, ColumnRef};
5
6#[derive(Debug, Clone, Serialize, Deserialize, TS)]
7#[ts(export)]
8pub struct TaskOp {
9    pub task_id: TaskId,
10    pub operation: Operation,
11    pub timestamp: u64,
12    pub client_id: u64,
13}
14
15#[derive(Debug, Clone, Serialize, Deserialize, TS)]
16#[ts(export)]
17pub enum Operation {
18    Create {
19        title: String,
20        column: String,
21    },
22    SetStatus {
23        old: Status,
24        new: Status,
25    },
26    SetTitle {
27        old: String,
28        new: String,
29    },
30    SetMetadata {
31        key: String,
32        #[ts(type = "unknown | null")]
33        old: Option<serde_json::Value>,
34        #[ts(type = "unknown | null")]
35        new: Option<serde_json::Value>,
36    },
37    AddDependency {
38        dependency: TaskId,
39    },
40    RemoveDependency {
41        dependency: TaskId,
42    },
43    Move {
44        from_column: String,
45        to_column: String,
46    },
47    Delete,
48}
49
50impl Operation {
51    pub fn apply_to_task(&self, task: &mut Task) {
52        let now = chrono::Utc::now();
53        match self {
54            Operation::SetTitle { old: _, new } => {
55                task.title = new.clone();
56                task.updated_at = now;
57            }
58            Operation::SetStatus { old: _, new } => {
59                task.status = *new;
60                task.updated_at = now;
61            }
62            Operation::SetMetadata { key, old: _, new } => {
63                match new {
64                    Some(value) => task.metadata.set(key, value.clone()),
65                    None => task.metadata.remove(key),
66                }
67                task.updated_at = now;
68            }
69            Operation::AddDependency { dependency } => {
70                if !task.dependencies.contains(dependency) {
71                    task.dependencies.push(dependency.clone());
72                    task.updated_at = now;
73                }
74            }
75            Operation::RemoveDependency { dependency } => {
76                let before = task.dependencies.len();
77                task.dependencies.retain(|d| d != dependency);
78                if task.dependencies.len() != before {
79                    task.updated_at = now;
80                }
81            }
82            Operation::Move { from_column: _, to_column } => {
83                task.column = ColumnRef::Name(to_column.clone());
84                task.updated_at = now;
85            }
86            Operation::Create { .. } | Operation::Delete => {}
87        }
88    }
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize, TS)]
92#[ts(export)]
93pub struct OperationId {
94    pub client_id: u64,
95    pub sequence: u64,
96}