use crate::ui::effect::{self, Effect}; use super::core::{Cmd, CmdContext}; #[cfg(test)] mod tests { use super::*; use crate::command::cmd::test_helpers::*; use crate::model::cell::{CellKey, CellValue}; #[test] fn clear_selected_cell_produces_clear_and_dirty() { let mut m = two_cat_model(); let key = CellKey::new(vec![ ("Type".to_string(), "Food".to_string()), ("Month".to_string(), "Jan".to_string()), ]); m.model.set_cell(key, CellValue::Number(42.0)); let layout = make_layout(&m); let reg = make_registry(); let ctx = make_ctx(&m, &layout, ®); let cmd = ClearCellCommand { key: ctx.cell_key().clone().unwrap(), }; let effects = cmd.execute(&ctx); assert_eq!(effects.len(), 2); } #[test] fn yank_cell_produces_set_yanked() { let mut m = two_cat_model(); let key = CellKey::new(vec![ ("Type".to_string(), "Food".to_string()), ("Month".to_string(), "Jan".to_string()), ]); m.model.set_cell(key, CellValue::Number(99.0)); let layout = make_layout(&m); let reg = make_registry(); let ctx = make_ctx(&m, &layout, ®); let cmd = YankCell { key: ctx.cell_key().clone().unwrap(), }; let effects = cmd.execute(&ctx); assert_eq!(effects.len(), 2); } #[test] fn paste_with_yanked_value_produces_set_cell() { let mut m = two_cat_model(); m.model.set_cell( CellKey::new(vec![ ("Type".into(), "Food".into()), ("Month".into(), "Jan".into()), ]), CellValue::Number(42.0), ); let layout = make_layout(&m); let reg = make_registry(); let yanked = Some(CellValue::Number(99.0)); let mut ctx = make_ctx(&m, &layout, ®); ctx.yanked = &yanked; let key = CellKey::new(vec![ ("Type".into(), "Clothing".into()), ("Month".into(), "Feb".into()), ]); let cmd = PasteCell { key }; let effects = cmd.execute(&ctx); assert_eq!(effects.len(), 2); let dbg = effects_debug(&effects); assert!(dbg.contains("SetCell"), "Expected SetCell, got: {dbg}"); } #[test] fn paste_without_yanked_value_produces_nothing() { let m = two_cat_model(); let layout = make_layout(&m); let reg = make_registry(); let ctx = make_ctx(&m, &layout, ®); let key = CellKey::new(vec![ ("Type".into(), "Food".into()), ("Month".into(), "Jan".into()), ]); let cmd = PasteCell { key }; let effects = cmd.execute(&ctx); assert!(effects.is_empty()); } #[test] fn transpose_produces_transpose_and_dirty() { let m = two_cat_model(); let layout = make_layout(&m); let reg = make_registry(); let ctx = make_ctx(&m, &layout, ®); let effects = TransposeAxes.execute(&ctx); assert_eq!(effects.len(), 2); let dbg = effects_debug(&effects); assert!( dbg.contains("TransposeAxes"), "Expected TransposeAxes, got: {dbg}" ); } #[test] fn save_produces_save_effect() { let m = two_cat_model(); let layout = make_layout(&m); let reg = make_registry(); let ctx = make_ctx(&m, &layout, ®); let effects = SaveCmd.execute(&ctx); assert_eq!(effects.len(), 1); let dbg = effects_debug(&effects); assert!(dbg.contains("Save"), "Expected Save, got: {dbg}"); } } // ── Cell operations ────────────────────────────────────────────────────────── // All cell commands take an explicit CellKey. The interactive spec fills it // from ctx.cell_key(); the parser fills it from Cat/Item coordinate args. /// Clear a cell. #[derive(Debug)] pub struct ClearCellCommand { pub key: crate::model::cell::CellKey, } impl Cmd for ClearCellCommand { fn name(&self) -> &'static str { "clear-cell" } fn execute(&self, _ctx: &CmdContext) -> Vec> { vec![ Box::new(effect::ClearCell(self.key.clone())), effect::mark_dirty(), ] } } /// Yank (copy) a cell value. #[derive(Debug)] pub struct YankCell { pub key: crate::model::cell::CellKey, } impl Cmd for YankCell { fn name(&self) -> &'static str { "yank" } fn execute(&self, ctx: &CmdContext) -> Vec> { let value = ctx.model.evaluate_aggregated(&self.key, ctx.none_cats()); vec![ Box::new(effect::SetYanked(value)), effect::set_status("Yanked"), ] } } /// Paste the yanked value into a cell. #[derive(Debug)] pub struct PasteCell { pub key: crate::model::cell::CellKey, } impl Cmd for PasteCell { fn name(&self) -> &'static str { "paste" } fn execute(&self, ctx: &CmdContext) -> Vec> { if let Some(value) = ctx.yanked.clone() { vec![ Box::new(effect::SetCell(self.key.clone(), value)), effect::mark_dirty(), ] } else { vec![] } } } // ── View commands ──────────────────────────────────────────────────────────── #[derive(Debug)] pub struct TransposeAxes; impl Cmd for TransposeAxes { fn name(&self) -> &'static str { "transpose" } fn execute(&self, _ctx: &CmdContext) -> Vec> { vec![Box::new(effect::TransposeAxes), effect::mark_dirty()] } } #[derive(Debug)] pub struct SaveCmd; impl Cmd for SaveCmd { fn name(&self) -> &'static str { "save" } fn execute(&self, _ctx: &CmdContext) -> Vec> { vec![Box::new(effect::Save)] } }