rsheet_lib/
command.rs

1use std::str::FromStr;
2
3use crate::cells::column_name_to_number;
4
5pub enum Command {
6    Get {
7        cell_identifier: CellIdentifier,
8    },
9    Set {
10        cell_identifier: CellIdentifier,
11        cell_expr: String,
12    },
13}
14
15/// A cell is uniquely identified by its (col index, row index) position in the
16/// spreadsheet grid. E.g. the cell "A2" can be represented as (0, 1), where
17/// rows and cols are 0-indexed.
18#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
19pub struct CellIdentifier {
20    pub col: u32,
21    pub row: u32,
22}
23
24impl FromStr for Command {
25    type Err = String;
26
27    fn from_str(s: &str) -> Result<Self, Self::Err> {
28        const MAX_PARTS: usize = 3;
29
30        let opt = (|| {
31            let mut parts = s.splitn(MAX_PARTS, |c: char| c.is_ascii_whitespace());
32            let command = parts.next()?;
33            let cell_identifier = parts.next()?.parse::<CellIdentifier>().ok()?;
34
35            let request = match command {
36                "get" => {
37                    let non_empty_rest = parts.next().is_some();
38                    if non_empty_rest {
39                        // Fail to parse if get has more arguments than expected.
40                        return None;
41                    }
42                    Self::Get { cell_identifier }
43                }
44                "set" => {
45                    let cell_expr = parts.next()?.to_string();
46                    Self::Set {
47                        cell_identifier,
48                        cell_expr,
49                    }
50                }
51                _ => return None,
52            };
53
54            Some(request)
55        })();
56
57        opt.ok_or_else(|| format!("Error parsing request: {s}"))
58    }
59}
60
61impl FromStr for CellIdentifier {
62    type Err = String;
63
64    fn from_str(s: &str) -> Result<Self, Self::Err> {
65        let opt = (|| {
66            let col_name = s
67                .chars()
68                .take_while(char::is_ascii_uppercase)
69                .collect::<String>();
70
71            if col_name.is_empty() {
72                return None;
73            }
74
75            let col = column_name_to_number(&col_name);
76
77            let row = s
78                .chars()
79                .skip_while(char::is_ascii_uppercase)
80                .collect::<String>()
81                .parse::<u32>()
82                .ok()?
83                .checked_sub(1)?;
84
85            Some(Self { col, row })
86        })();
87
88        opt.ok_or_else(|| format!("Error parsing cell position: {s}"))
89    }
90}