use super::CommandContext;
use crate::ColumnUpdate;
use crate::KanbanResult;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "action", rename_all = "snake_case")]
pub enum ColumnCommand {
Create(CreateColumn),
Update(UpdateColumn),
Delete(DeleteColumn),
}
impl ColumnCommand {
pub fn execute(&self, context: &CommandContext) -> KanbanResult<()> {
match self {
ColumnCommand::Create(c) => c.execute(context),
ColumnCommand::Update(c) => c.execute(context),
ColumnCommand::Delete(c) => c.execute(context),
}
}
pub fn description(&self) -> String {
match self {
ColumnCommand::Create(c) => c.description(),
ColumnCommand::Update(c) => c.description(),
ColumnCommand::Delete(c) => c.description(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateColumn {
pub column_id: Uuid,
pub updates: ColumnUpdate,
}
impl UpdateColumn {
pub fn execute(&self, context: &CommandContext) -> KanbanResult<()> {
let mut column = context.get_column(self.column_id)?;
column.update(self.updates.clone());
context.store.upsert_column(column)?;
Ok(())
}
pub fn description(&self) -> String {
"Update column".to_string()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateColumn {
pub id: Uuid,
pub board_id: Uuid,
pub name: String,
pub position: i32,
}
impl CreateColumn {
pub fn execute(&self, context: &CommandContext) -> KanbanResult<()> {
let mut column = crate::Column::new(self.board_id, self.name.clone(), self.position);
column.id = self.id;
context.store.upsert_column(column)?;
Ok(())
}
pub fn description(&self) -> String {
format!("Create column: '{}'", self.name)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeleteColumn {
pub column_id: Uuid,
}
impl DeleteColumn {
pub fn execute(&self, context: &CommandContext) -> KanbanResult<()> {
let has_cards = context.store.count_cards_in_column(self.column_id)? > 0;
if has_cards {
return Err(crate::KanbanError::validation(format!(
"Cannot delete column {}: column contains cards",
self.column_id
)));
}
let has_archived_cards = context
.store
.list_archived_cards()?
.iter()
.any(|ac| ac.original_column_id == self.column_id);
if has_archived_cards {
return Err(crate::KanbanError::validation(format!(
"Cannot delete column {}: column contains archived cards",
self.column_id
)));
}
context.store.delete_column(self.column_id)?;
Ok(())
}
pub fn description(&self) -> String {
format!("Delete column {}", self.column_id)
}
}
#[cfg(test)]
mod tests {
use super::super::test_helpers::TestContext;
use super::*;
#[test]
fn test_update_column_not_found_returns_error() {
let tc = TestContext::new();
let context = tc.as_command_context();
let cmd = UpdateColumn {
column_id: Uuid::new_v4(),
updates: ColumnUpdate::default(),
};
let result = cmd.execute(&context);
assert!(result.unwrap_err().is_not_found());
}
}