use crate::view::Axis; use serde::{Deserialize, Serialize}; /// All commands that can mutate a Model. /// /// Serialized as `{"op": "", ...rest}` where `rest` contains /// the variant's fields flattened into the same JSON object. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "op")] pub enum Command { /// Add a category (dimension). AddCategory { name: String }, /// Add an item to a category. AddItem { category: String, item: String }, /// Add an item inside a named group. AddItemInGroup { category: String, item: String, group: String, }, /// Set a cell value. `coords` is a list of `[category, item]` pairs. SetCell { coords: Vec<[String; 2]>, #[serde(flatten)] value: CellValueArg, }, /// Clear a cell. ClearCell { coords: Vec<[String; 2]> }, /// Add or replace a formula. /// `raw` is the full formula string, e.g. "Profit = Revenue - Cost". /// `target_category` names the category that owns the formula target. AddFormula { raw: String, target_category: String, }, /// Remove a formula by its target name and category. RemoveFormula { target: String, target_category: String, }, /// Create a new view. CreateView { name: String }, /// Delete a view. DeleteView { name: String }, /// Switch the active view. SwitchView { name: String }, /// Set the axis of a category in the active view. SetAxis { category: String, axis: Axis }, /// Set the page-axis selection for a category. SetPageSelection { category: String, item: String }, /// Toggle collapse of a group in the active view. ToggleGroup { category: String, group: String }, /// Hide an item in the active view. HideItem { category: String, item: String }, /// Show (un-hide) an item in the active view. ShowItem { category: String, item: String }, /// Save the model to a file path. Save { path: String }, /// Load a model from a file path (replaces current model). Load { path: String }, /// Export the active view to CSV. ExportCsv { path: String }, /// Import a JSON file via the analyzer (non-interactive, uses auto-detected proposals). ImportJson { path: String, model_name: Option, /// Dot-path to the records array (empty = root) array_path: Option, }, } /// Inline value for SetCell #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] pub enum CellValueArg { Number { number: f64 }, Text { text: String }, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CommandResult { pub ok: bool, #[serde(skip_serializing_if = "Option::is_none")] pub message: Option, } impl CommandResult { pub fn ok() -> Self { Self { ok: true, message: None, } } pub fn ok_msg(msg: impl Into) -> Self { Self { ok: true, message: Some(msg.into()), } } pub fn err(msg: impl Into) -> Self { Self { ok: false, message: Some(msg.into()), } } }