use std::collections::{HashMap, HashSet};
use logisheets_base::{errors::BasicError, BlockId, CellId, ColId, RowId, SheetId};
use crate::{
controller::status::Status,
edit_action::{EditPayload, PayloadsAction},
Error,
};
use super::ctx::{VersionExecCtx, VersionExecCtxImpl};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Diff {
CellValue(CellId),
CellStyle(CellId),
RowInfo(RowId),
ColInfo(ColId),
BlockUpdate {
id: BlockId,
cnt: usize,
is_row: bool,
},
SheetProperty, Unavailable,
}
#[derive(Debug, Clone, Default)]
pub struct SheetDiff {
pub data: HashSet<Diff>,
}
impl SheetDiff {
pub fn insert_diff(&mut self, diff: Diff) {
if diff == Diff::Unavailable {
self.data = HashSet::from([diff]);
} else if !self.data.contains(&Diff::Unavailable) {
self.data.insert(diff);
}
}
pub fn diff_unavailable(&self) -> bool {
self.data.contains(&Diff::Unavailable)
}
}
pub fn convert_payloads_to_sheet_diff(
status: &mut Status,
process: PayloadsAction,
updated_cells: HashSet<(SheetId, CellId)>,
) -> HashMap<SheetId, SheetDiff> {
let mut result: HashMap<SheetId, SheetDiff> = HashMap::new();
let ctx = VersionExecCtxImpl::new(status);
process.payloads.into_iter().for_each(|sp| {
if let Ok(Some((diff, sheet_id))) = convert_diff(sp, &ctx) {
let sheet_diff = result.entry(sheet_id).or_insert(SheetDiff::default());
sheet_diff.insert_diff(diff);
}
});
updated_cells.into_iter().for_each(|(sheet_id, cell_id)| {
let diff = Diff::CellValue(cell_id);
let sheet_diff = result.entry(sheet_id).or_insert(SheetDiff::default());
sheet_diff.insert_diff(diff);
});
result
}
#[inline]
fn convert_diff<C: VersionExecCtx>(
payload: EditPayload,
ctx: &C,
) -> Result<Option<(Diff, SheetId)>, Error> {
match payload {
EditPayload::BlockInput(bi) => {
let sheet_id = ctx
.fetch_sheet_id_by_index(bi.sheet_idx)
.map_err(|l| BasicError::SheetIdxExceed(l))?;
let cell_id = ctx.fetch_block_cell_id(&sheet_id, &bi.block_id, bi.row, bi.col)?;
Ok(Some((
Diff::CellValue(CellId::BlockCell(cell_id)),
sheet_id,
)))
}
EditPayload::MoveBlock(p) => {
let sheet_id = ctx
.fetch_sheet_id_by_index(p.sheet_idx)
.map_err(|l| BasicError::SheetIdxExceed(l))?;
Ok(Some((Diff::Unavailable, sheet_id)))
}
EditPayload::RemoveBlock(p) => {
let sheet_id = ctx
.fetch_sheet_id_by_index(p.sheet_idx)
.map_err(|l| BasicError::SheetIdxExceed(l))?;
Ok(Some((Diff::Unavailable, sheet_id)))
}
EditPayload::CreateBlock(p) => {
let sheet_id = ctx
.fetch_sheet_id_by_index(p.sheet_idx)
.map_err(|l| BasicError::SheetIdxExceed(l))?;
Ok(Some((Diff::Unavailable, sheet_id)))
}
EditPayload::StyleUpdate(p) => {
let sheet_id = ctx
.fetch_sheet_id_by_index(p.sheet_idx)
.map_err(|l| BasicError::SheetIdxExceed(l))?;
let id = ctx.fetch_cell_id(&sheet_id, p.row, p.col)?;
Ok(Some((Diff::CellStyle(id), sheet_id)))
}
EditPayload::BlockStyleUpdate(p) => {
let sheet_id = ctx
.fetch_sheet_id_by_index(p.sheet_idx)
.map_err(|l| BasicError::SheetIdxExceed(l))?;
let id = ctx.fetch_block_cell_id(&sheet_id, &p.block_id, p.row, p.col)?;
Ok(Some((Diff::CellStyle(CellId::BlockCell(id)), sheet_id)))
}
EditPayload::CellInput(ci) => {
let sheet_id = ctx
.fetch_sheet_id_by_index(ci.sheet_idx)
.map_err(|l| BasicError::SheetIdxExceed(l))?;
let cell_id = ctx.fetch_cell_id(&sheet_id, ci.row, ci.col)?;
Ok(Some((Diff::CellValue(cell_id), sheet_id)))
}
EditPayload::SetColWidth(col) => {
let sheet_id = ctx
.fetch_sheet_id_by_index(col.sheet_idx)
.map_err(|l| BasicError::SheetIdxExceed(l))?;
let col_id = ctx.fetch_col_id(&sheet_id, col.col)?;
Ok(Some((Diff::ColInfo(col_id), sheet_id)))
}
EditPayload::SetRowHeight(row) => {
let sheet_id = ctx
.fetch_sheet_id_by_index(row.sheet_idx)
.map_err(|l| BasicError::SheetIdxExceed(l))?;
let row_id = ctx.fetch_row_id(&sheet_id, row.row)?;
Ok(Some((Diff::RowInfo(row_id), sheet_id)))
}
EditPayload::SetVisible(p) => {
let sheet_id = ctx
.fetch_sheet_id_by_index(p.sheet_idx)
.map_err(|l| BasicError::SheetIdxExceed(l))?;
Ok(Some((Diff::SheetProperty, sheet_id)))
}
EditPayload::SheetRename(_) => Ok(Some((Diff::SheetProperty, 0))),
EditPayload::CreateSheet(_) => Ok(Some((Diff::SheetProperty, 0))),
EditPayload::DeleteSheet(_) => Ok(Some((Diff::SheetProperty, 0))),
EditPayload::InsertCols(p) => {
let sheet_id = ctx
.fetch_sheet_id_by_index(p.sheet_idx)
.map_err(|l| BasicError::SheetIdxExceed(l))?;
Ok(Some((Diff::Unavailable, sheet_id)))
}
EditPayload::DeleteCols(p) => {
let sheet_id = ctx
.fetch_sheet_id_by_index(p.sheet_idx)
.map_err(|l| BasicError::SheetIdxExceed(l))?;
Ok(Some((Diff::Unavailable, sheet_id)))
}
EditPayload::InsertRows(p) => {
let sheet_id = ctx
.fetch_sheet_id_by_index(p.sheet_idx)
.map_err(|l| BasicError::SheetIdxExceed(l))?;
Ok(Some((Diff::Unavailable, sheet_id)))
}
EditPayload::DeleteRows(p) => {
let sheet_id = ctx
.fetch_sheet_id_by_index(p.sheet_idx)
.map_err(|l| BasicError::SheetIdxExceed(l))?;
Ok(Some((Diff::Unavailable, sheet_id)))
}
EditPayload::InsertColsInBlock(p) => {
let sheet_id = ctx
.fetch_sheet_id_by_index(p.sheet_idx)
.map_err(|l| BasicError::SheetIdxExceed(l))?;
Ok(Some((Diff::Unavailable, sheet_id)))
}
EditPayload::DeleteColsInBlock(p) => {
let sheet_id = ctx
.fetch_sheet_id_by_index(p.sheet_idx)
.map_err(|l| BasicError::SheetIdxExceed(l))?;
Ok(Some((Diff::Unavailable, sheet_id)))
}
EditPayload::InsertRowsInBlock(p) => {
let sheet_id = ctx
.fetch_sheet_id_by_index(p.sheet_idx)
.map_err(|l| BasicError::SheetIdxExceed(l))?;
Ok(Some((Diff::Unavailable, sheet_id)))
}
EditPayload::DeleteRowsInBlock(p) => {
let sheet_id = ctx
.fetch_sheet_id_by_index(p.sheet_idx)
.map_err(|l| BasicError::SheetIdxExceed(l))?;
Ok(Some((Diff::Unavailable, sheet_id)))
}
EditPayload::CellClear(cr) => {
let sheet_id = ctx
.fetch_sheet_id_by_index(cr.sheet_idx)
.map_err(|l| BasicError::SheetIdxExceed(l))?;
let cell_id = ctx.fetch_cell_id(&sheet_id, cr.row, cr.col)?;
Ok(Some((Diff::CellValue(cell_id), sheet_id)))
}
}
}