logisheets_controller 1.16.1

Core spreadsheet engine for LogiSheets: formula evaluation, dependency tracking, and undo/redo.
Documentation
use imbl::Vector;
use logisheets_base::errors::BasicError;

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

use super::{
    CellAttachmentsManager,
    comment::{CommentNote, Mention, PersonInput},
    ctx::CellAttachmentsExecCtx,
};

fn to_person_input(a: AuthorInput) -> PersonInput {
    PersonInput {
        display_name: a.display_name,
        user_id: a.user_id,
        provider_id: a.provider_id,
    }
}

fn resolve_mentions(
    manager: &mut CellAttachmentsManager,
    mentions: Vec<CommentMention>,
) -> Vector<Mention> {
    mentions
        .into_iter()
        .map(|m| {
            let person = manager
                .comments
                .persons
                .get_or_register(to_person_input(m.author));
            let mention_id = m
                .mention_id
                .unwrap_or_else(|| format!("{{{}}}", uuid::Uuid::new_v4()));
            Mention {
                person,
                start: m.start,
                len: m.len,
                mention_id,
            }
        })
        .collect()
}

pub struct CellAttachmentsExecutor {
    pub manager: CellAttachmentsManager,
}

impl CellAttachmentsExecutor {
    pub fn new(manager: CellAttachmentsManager) -> Self {
        Self { manager }
    }

    pub fn execute<C: CellAttachmentsExecCtx>(
        mut self,
        ctx: &mut C,
        payload: EditPayload,
    ) -> Result<(Self, bool), Error> {
        match payload {
            EditPayload::MergeCells(merge_cells) => {
                let sheet_id = ctx
                    .fetch_sheet_id_by_index(merge_cells.sheet_idx)
                    .map_err(|l| BasicError::SheetIdxExceed(l))?;
                let start_cell_id = ctx.fetch_norm_cell_id(
                    &sheet_id,
                    merge_cells.start_row,
                    merge_cells.start_col,
                )?;
                let end_cell_id =
                    ctx.fetch_norm_cell_id(&sheet_id, merge_cells.end_row, merge_cells.end_col)?;
                self.manager
                    .merge_cells
                    .add_merge_cell(sheet_id, start_cell_id, end_cell_id);
                Ok((self, true))
            }
            EditPayload::SplitMergedCells(p) => {
                let sheet_id = ctx
                    .fetch_sheet_id_by_index(p.sheet_idx)
                    .map_err(|l| BasicError::SheetIdxExceed(l))?;
                let cell_id = ctx.fetch_norm_cell_id(&sheet_id, p.row, p.col)?;

                if self
                    .manager
                    .merge_cells
                    .get_merge_cell(&sheet_id, &cell_id)
                    .is_some()
                {
                    self.manager.merge_cells = self
                        .manager
                        .merge_cells
                        .remove_merge_cell(sheet_id, cell_id);
                    Ok((self, true))
                } else {
                    Ok((self, false))
                }
            }
            EditPayload::AddComment(p) => {
                let sheet_id = ctx
                    .fetch_sheet_id_by_index(p.sheet_idx)
                    .map_err(|l| BasicError::SheetIdxExceed(l))?;
                let cell_id = ctx.fetch_cell_id(&sheet_id, p.row, p.col)?;
                let person = self
                    .manager
                    .comments
                    .persons
                    .get_or_register(to_person_input(p.author));
                let mentions = resolve_mentions(&mut self.manager, p.mentions);
                let note = CommentNote {
                    id: p.comment_id,
                    person,
                    dt: p.dt,
                    text: p.content,
                    parent: p.parent_id,
                    mentions,
                    resolved: false,
                };
                self.manager.comments.add_note(sheet_id, cell_id, note);
                Ok((self, true))
            }
            EditPayload::EditComment(p) => {
                let sheet_id = ctx
                    .fetch_sheet_id_by_index(p.sheet_idx)
                    .map_err(|l| BasicError::SheetIdxExceed(l))?;
                let mentions = resolve_mentions(&mut self.manager, p.mentions);
                let changed =
                    self.manager
                        .comments
                        .edit_note(sheet_id, &p.comment_id, p.content, mentions);
                Ok((self, changed))
            }
            EditPayload::DeleteComment(p) => {
                let sheet_id = ctx
                    .fetch_sheet_id_by_index(p.sheet_idx)
                    .map_err(|l| BasicError::SheetIdxExceed(l))?;
                let changed = self.manager.comments.delete_note(sheet_id, &p.comment_id);
                Ok((self, changed))
            }
            EditPayload::ResolveComment(p) => {
                let sheet_id = ctx
                    .fetch_sheet_id_by_index(p.sheet_idx)
                    .map_err(|l| BasicError::SheetIdxExceed(l))?;
                let changed =
                    self.manager
                        .comments
                        .set_resolved(sheet_id, &p.comment_id, p.resolved);
                Ok((self, changed))
            }
            EditPayload::UpsertPerson(p) => {
                self.manager.comments.persons.get_or_register(PersonInput {
                    display_name: p.display_name,
                    user_id: p.user_id,
                    provider_id: p.provider_id,
                });
                Ok((self, true))
            }
            _ => Ok((self, false)),
        }
    }
}