rsheet_lib 0.2.0

Libraries to help implementing cs6991-24T1-ass2
Documentation
use std::str::FromStr;

use crate::cells::column_name_to_number;

pub enum Command {
    Get {
        cell_identifier: CellIdentifier,
    },
    Set {
        cell_identifier: CellIdentifier,
        cell_expr: String,
    },
}

/// A cell is uniquely identified by its (col index, row index) position in the
/// spreadsheet grid. E.g. the cell "A2" can be represented as (0, 1), where
/// rows and cols are 0-indexed.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
pub struct CellIdentifier {
    pub col: u32,
    pub row: u32,
}

impl FromStr for Command {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        const MAX_PARTS: usize = 3;

        let opt = (|| {
            let mut parts = s.splitn(MAX_PARTS, |c: char| c.is_ascii_whitespace());
            let command = parts.next()?;
            let cell_identifier = parts.next()?.parse::<CellIdentifier>().ok()?;

            let request = match command {
                "get" => {
                    let non_empty_rest = parts.next().is_some();
                    if non_empty_rest {
                        // Fail to parse if get has more arguments than expected.
                        return None;
                    }
                    Self::Get { cell_identifier }
                }
                "set" => {
                    let cell_expr = parts.next()?.to_string();
                    Self::Set {
                        cell_identifier,
                        cell_expr,
                    }
                }
                _ => return None,
            };

            Some(request)
        })();

        opt.ok_or_else(|| format!("Error parsing request: {s}"))
    }
}

impl FromStr for CellIdentifier {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let opt = (|| {
            let col_name = s
                .chars()
                .take_while(char::is_ascii_uppercase)
                .collect::<String>();

            if col_name.is_empty() {
                return None;
            }

            let col = column_name_to_number(&col_name);

            let row = s
                .chars()
                .skip_while(char::is_ascii_uppercase)
                .collect::<String>()
                .parse::<u32>()
                .ok()?
                .checked_sub(1)?;

            Some(Self { col, row })
        })();

        opt.ok_or_else(|| format!("Error parsing cell position: {s}"))
    }
}