logisheets_controller 0.7.0

the core of LogiSheets
Documentation
use crate::lock::{locked_read, locked_write, Locked};

use super::sheet_nav::{Cache, Data};
use logisheets_base::{
    errors::BasicError, BlockCellId, CellId, ColId, NormalCellId, RowId, SheetId,
};

type Result<T> = std::result::Result<T, BasicError>;

pub struct Fetcher<'a> {
    sheet_id: SheetId,
    data: &'a Data,
    cache: &'a Locked<Cache>,
}

impl<'a> Fetcher<'a> {
    pub fn from(data: &'a Data, cache: &'a Locked<Cache>, sheet_id: SheetId) -> Self {
        Fetcher {
            data,
            cache,
            sheet_id,
        }
    }

    pub fn get_row_id(&self, row: usize) -> RowId {
        let cache = locked_read(&self.cache);
        let cache_id = cache.row_id.get(&row);
        match cache_id {
            Some(result) => *result,
            None => {
                drop(cache);
                let result = *self.data.rows.get(row).unwrap();
                let mut cache = locked_write(&self.cache);
                cache.row_id.insert(row, result);
                cache.row_index.insert(result, row);
                result
            }
        }
    }

    pub fn get_row_idx(&self, row: RowId) -> Result<usize> {
        let cache = locked_read(&self.cache);
        let cache_idx = cache.row_index.get(&row);
        match cache_idx {
            Some(r) => Ok(*r),
            None => {
                drop(cache);
                let idx = self.data.rows.iter().position(|e| *e == row);
                match idx {
                    Some(r) => {
                        let mut cache = locked_write(&self.cache);
                        cache.row_index.insert(row, r);
                        cache.row_id.insert(r, row);
                        drop(cache);
                        Ok(r)
                    }
                    None => Err(BasicError::RowIndexUnavailable(row)),
                }
            }
        }
    }

    pub fn get_col_id(&self, col: usize) -> ColId {
        let cache = locked_read(&self.cache);
        let cache_id = cache.col_id.get(&col);
        match cache_id {
            Some(result) => *result,
            None => {
                drop(cache);
                let result = *self.data.cols.get(col).unwrap();
                let mut cache = locked_write(&self.cache);
                cache.col_id.insert(col, result);
                cache.col_index.insert(result, col);
                result
            }
        }
    }

    pub fn get_col_idx(&self, col: ColId) -> Result<usize> {
        let cache = locked_read(&self.cache);
        let cache_idx = cache.col_index.get(&col);
        match cache_idx {
            Some(r) => Ok(*r),
            None => {
                let idx = self.data.cols.iter().position(|e| *e == col);
                match idx {
                    Some(r) => {
                        drop(cache);
                        let mut cache = locked_write(&self.cache);
                        cache.col_index.insert(col, r);
                        cache.col_id.insert(r, col);
                        Ok(r)
                    }
                    None => Err(BasicError::ColIndexUnavailable(col)),
                }
            }
        }
    }

    pub fn get_cell_id(&self, row: usize, col: usize) -> Result<CellId> {
        let cache = locked_read(&self.cache);
        if let Some(r) = cache.cell_id.get(&(row, col)) {
            return Ok(r.clone());
        }
        drop(cache);
        let mut res: Option<CellId> = None;
        for (id, bp) in self.data.blocks.iter() {
            let master = &bp.master;
            let (ridx, cidx) = self.get_norm_cell_idx(master)?;
            if row < ridx || col < cidx {
                continue;
            } else {
                match bp.get_inner_id(row - ridx, col - cidx) {
                    Some((rid, cid)) => {
                        let bid = BlockCellId {
                            block_id: *id,
                            row: rid,
                            col: cid,
                        };
                        res = Some(CellId::BlockCell(bid));
                        break;
                    }
                    None => {}
                }
            }
        }
        if let Some(res) = res {
            let mut cache = locked_write(&self.cache);
            cache.cell_id.insert((row, col), res);
            drop(cache);
            return Ok(res);
        }
        let res = CellId::NormalCell(self.get_norm_cell_id(row, col));
        let mut cache = locked_write(&self.cache);
        cache.cell_id.insert((row, col), res);
        drop(cache);
        Ok(res)
    }

    pub fn get_norm_cell_id(&self, row: usize, col: usize) -> NormalCellId {
        let row_id = self.get_row_id(row);
        let col_id = self.get_col_id(col);
        NormalCellId {
            row: row_id,
            col: col_id,
        }
    }

    pub fn get_block_cell_idx(&self, block_cell_id: &BlockCellId) -> Result<(usize, usize)> {
        let bid = block_cell_id.block_id;
        let bp = self
            .data
            .blocks
            .get(&bid)
            .ok_or(BasicError::BlockIdNotFound(self.sheet_id, bid))?;
        let master = &bp.master;
        let (m_row, m_col) = self.get_norm_cell_idx(master)?;
        let (row_idx, col_idx) = bp
            .get_inner_idx(block_cell_id.row, block_cell_id.col)
            .ok_or(BasicError::CannotFindIdxInBlock(
                self.sheet_id,
                bid,
                block_cell_id.row,
                block_cell_id.col,
            ))?;
        Ok((row_idx + m_row, col_idx + m_col))
    }

    pub fn get_cell_idx(&self, cell_id: &CellId) -> Result<(usize, usize)> {
        let cache = locked_read(&self.cache);
        if let Some(r) = cache.cell_idx.get(cell_id) {
            return Ok(r.clone());
        }
        drop(cache);
        let res = match cell_id {
            CellId::NormalCell(c) => self.get_norm_cell_idx(c),
            CellId::BlockCell(b) => self.get_block_cell_idx(b),
        }?;
        let mut cache = locked_write(&self.cache);
        cache.cell_idx.insert(cell_id.clone(), res);
        drop(cache);
        Ok(res)
    }

    pub fn get_norm_cell_idx(&self, cell_id: &NormalCellId) -> Result<(usize, usize)> {
        let row_idx = self.get_row_idx(cell_id.row)?;
        let col_idx = self.get_col_idx(cell_id.col)?;
        Ok((row_idx, col_idx))
    }
}