Skip to main content

boarddown_core/crdt/
changeset.rs

1use serde::{Deserialize, Serialize};
2
3use crate::Version;
4use boarddown_schema::{TaskOp, Operation, TaskBuilder};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct Changeset {
8    pub board_id: boarddown_schema::BoardId,
9    pub from_version: Version,
10    pub to_version: Version,
11    pub operations: Vec<TaskOp>,
12    pub timestamp: chrono::DateTime<chrono::Utc>,
13}
14
15impl Changeset {
16    pub fn new(board_id: boarddown_schema::BoardId, from_version: Version) -> Self {
17        Self {
18            board_id,
19            from_version,
20            to_version: from_version.increment(),
21            operations: Vec::new(),
22            timestamp: chrono::Utc::now(),
23        }
24    }
25
26    pub fn add_operation(&mut self, op: TaskOp) {
27        self.operations.push(op);
28    }
29
30    pub fn is_empty(&self) -> bool {
31        self.operations.is_empty()
32    }
33
34    pub fn apply(&self, board: &mut boarddown_schema::Board) -> Result<(), crate::Error> {
35        for task_op in &self.operations {
36            match &task_op.operation {
37                Operation::Create { title, column } => {
38                    let task = TaskBuilder::default()
39                        .id(task_op.task_id.clone())
40                        .title(title.clone())
41                        .column(column.clone())
42                        .build()
43                        .map_err(|e| crate::Error::Validation(e))?;
44                    board.tasks.insert(task.id.clone(), task);
45                }
46                Operation::Delete => {
47                    board.tasks.remove(&task_op.task_id);
48                }
49                _ => {
50                    if let Some(task) = board.tasks.get_mut(&task_op.task_id) {
51                        task_op.operation.apply_to_task(task);
52                    }
53                }
54            }
55        }
56        board.updated_at = chrono::Utc::now();
57        Ok(())
58    }
59
60    pub fn merge(&self, other: &Self) -> Result<Self, crate::Error> {
61        let mut all_ops: Vec<TaskOp> = self.operations.iter()
62            .chain(other.operations.iter())
63            .cloned()
64            .collect();
65        
66        all_ops.sort_by_key(|op| op.timestamp);
67        
68        let max_version = self.to_version.0.max(other.to_version.0);
69        
70        Ok(Self {
71            board_id: self.board_id.clone(),
72            from_version: Version(max_version),
73            to_version: Version(max_version + 1),
74            operations: all_ops,
75            timestamp: chrono::Utc::now(),
76        })
77    }
78}