logisheets_controller 1.16.1

Core spreadsheet engine for LogiSheets: formula evaluation, dependency tracking, and undo/redo.
Documentation
mod delete_line;
mod delete_sheet;
mod input;
mod insert_line;
mod utils;
use std::collections::HashSet;

use delete_line::delete_line;
use delete_sheet::delete_sheet;
use input::input;
use insert_line::insert_line;
use logisheets_base::{Cube, CubeId, errors::BasicError};

use crate::{Error, edit_action::EditPayload};

use super::{ctx::CubeExecCtx, manager::CubeManager};

#[derive(Debug, Clone)]
pub struct CubeExecutor {
    pub manager: CubeManager,
    pub dirty_cubes: HashSet<CubeId>,
    pub removed_cubes: HashSet<CubeId>,
}

impl CubeExecutor {
    pub fn new(manager: CubeManager) -> Self {
        CubeExecutor {
            manager,
            dirty_cubes: HashSet::new(),
            removed_cubes: HashSet::new(),
        }
    }

    pub fn execute<C: CubeExecCtx>(
        self,
        ctx: &C,
        payload: EditPayload,
    ) -> Result<CubeExecutor, Error> {
        match payload {
            EditPayload::CellInput(cell_input) => {
                let sheet_idx = cell_input.sheet_idx;
                let sheet_id = ctx
                    .fetch_sheet_id_by_index(sheet_idx)
                    .map_err(|l| BasicError::SheetIdxExceed(l))?;
                input(self, sheet_id, cell_input.row, cell_input.col, ctx)
            }
            EditPayload::ReproduceCells(p) => {
                if p.cells.is_empty() {
                    return Ok(self);
                }
                let sheet_idx = p.sheet_idx;
                let sheet_id = ctx
                    .fetch_sheet_id_by_index(sheet_idx)
                    .map_err(|l| BasicError::SheetIdxExceed(l))?;
                let anchor_x = p.cells[0].coordinate.row;
                let anchor_y = p.cells[0].coordinate.col;
                let mut res = self;
                for cell in p.cells {
                    res = input(
                        res,
                        sheet_id,
                        cell.coordinate.row - anchor_x + p.start_row,
                        cell.coordinate.col - anchor_y + p.start_col,
                        ctx,
                    )?;
                }
                Ok(res)
            }
            EditPayload::CellClear(cell_clear) => {
                let sheet_idx = cell_clear.sheet_idx;
                let sheet_id = ctx
                    .fetch_sheet_id_by_index(sheet_idx)
                    .map_err(|l| BasicError::SheetIdxExceed(l))?;
                input(self, sheet_id, cell_clear.row, cell_clear.col, ctx)
            }
            EditPayload::InsertCols(insert_cols) => {
                let sheet_idx = insert_cols.sheet_idx;
                let sheet_id = ctx
                    .fetch_sheet_id_by_index(sheet_idx)
                    .map_err(|l| BasicError::SheetIdxExceed(l))?;
                insert_line(
                    self,
                    sheet_id,
                    false,
                    insert_cols.start,
                    insert_cols.count as u32,
                    ctx,
                )
            }
            EditPayload::DeleteCols(delete_cols) => {
                let sheet_idx = delete_cols.sheet_idx;
                let sheet_id = ctx
                    .fetch_sheet_id_by_index(sheet_idx)
                    .map_err(|l| BasicError::SheetIdxExceed(l))?;
                delete_line(
                    self,
                    sheet_id,
                    false,
                    delete_cols.start,
                    delete_cols.count as u32,
                    ctx,
                )
            }
            EditPayload::InsertRows(insert_rows) => {
                let sheet_idx = insert_rows.sheet_idx;
                let sheet_id = ctx
                    .fetch_sheet_id_by_index(sheet_idx)
                    .map_err(|l| BasicError::SheetIdxExceed(l))?;
                insert_line(
                    self,
                    sheet_id,
                    true,
                    insert_rows.start,
                    insert_rows.count as u32,
                    ctx,
                )
            }
            EditPayload::DeleteRows(delete_rows) => {
                let sheet_idx = delete_rows.sheet_idx;
                let sheet_id = ctx
                    .fetch_sheet_id_by_index(sheet_idx)
                    .map_err(|l| BasicError::SheetIdxExceed(l))?;
                delete_line(
                    self,
                    sheet_id,
                    true,
                    delete_rows.start,
                    delete_rows.count as u32,
                    ctx,
                )
            }
            EditPayload::DeleteSheet(p) => {
                let sheet_id = ctx
                    .fetch_sheet_id_by_index(p.idx)
                    .map_err(|l| BasicError::SheetIdxExceed(l))?;
                delete_sheet(self, sheet_id, ctx)
            }
            _ => Ok(self),
        }
    }

    pub fn cube_update<F>(self, func: &mut F) -> Result<Self, Error>
    where
        F: FnMut(&Cube, &CubeId) -> Result<CubeUpdateType, Error>,
    {
        let mut dirty_cubes = self.dirty_cubes;
        let removed_cubes = self.removed_cubes;
        let new_manager = self.manager.clone();
        for (c, id) in self.manager.cube_to_id.iter() {
            match func(c, id)? {
                CubeUpdateType::Dirty => {
                    dirty_cubes.insert(*id);
                }
                CubeUpdateType::None => {}
            }
        }
        Ok(CubeExecutor {
            manager: new_manager,
            dirty_cubes,
            removed_cubes,
        })
    }
}

pub enum CubeUpdateType {
    None,
    Dirty,
}