1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
use std::cmp::Ordering;

use crate::expressions::token::Error;

#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub struct CellReference {
    pub sheet: u32,
    pub column: i32,
    pub row: i32,
}

#[derive(Debug, Clone)]
pub struct Range {
    pub left: CellReference,
    pub right: CellReference,
}

#[derive(Clone)]
pub(crate) enum CalcResult {
    String(String),
    Number(f64),
    Boolean(bool),
    Error {
        error: Error,
        origin: CellReference,
        message: String,
    },
    Range {
        left: CellReference,
        right: CellReference,
    },
    EmptyCell,
    EmptyArg,
}

impl CalcResult {
    pub fn new_error(error: Error, origin: CellReference, message: String) -> CalcResult {
        CalcResult::Error {
            error,
            origin,
            message,
        }
    }
    pub fn new_args_number_error(origin: CellReference) -> CalcResult {
        CalcResult::Error {
            error: Error::ERROR,
            origin,
            message: "Wrong number of arguments".to_string(),
        }
    }
    pub fn is_error(&self) -> bool {
        matches!(self, CalcResult::Error { .. })
    }
}

impl Ord for CalcResult {
    // ..., -2, -1, 0, 1, 2, ..., A-Z, FALSE, TRUE, empty;
    fn cmp(&self, other: &Self) -> Ordering {
        match (self, other) {
            (CalcResult::Number(value1), CalcResult::Number(value2)) => {
                if (value2 - value1).abs() < f64::EPSILON {
                    return Ordering::Equal;
                }
                if value1 < value2 {
                    return Ordering::Less;
                }
                Ordering::Greater
            }
            (CalcResult::Number(_value1), CalcResult::String(_value2)) => Ordering::Less,
            (CalcResult::Number(_value1), CalcResult::Boolean(_value2)) => Ordering::Less,
            (CalcResult::String(value1), CalcResult::String(value2)) => {
                let value1 = value1.to_uppercase();
                let value2 = value2.to_uppercase();
                value1.cmp(&value2)
            }
            (CalcResult::String(_value1), CalcResult::Boolean(_value2)) => Ordering::Less,
            (CalcResult::Boolean(value1), CalcResult::Boolean(value2)) => {
                if value1 == value2 {
                    return Ordering::Equal;
                }
                if *value1 {
                    return Ordering::Greater;
                }
                Ordering::Less
            }
            (CalcResult::EmptyCell, CalcResult::String(_value2)) => Ordering::Greater,
            (CalcResult::EmptyArg, CalcResult::String(_value2)) => Ordering::Greater,
            (CalcResult::String(_value1), CalcResult::EmptyCell) => Ordering::Less,
            (CalcResult::EmptyCell, CalcResult::Number(_value2)) => Ordering::Greater,
            (CalcResult::EmptyArg, CalcResult::Number(_value2)) => Ordering::Greater,
            (CalcResult::Number(_value1), CalcResult::EmptyCell) => Ordering::Less,
            (CalcResult::EmptyCell, CalcResult::EmptyCell) => Ordering::Equal,
            (CalcResult::EmptyCell, CalcResult::EmptyArg) => Ordering::Equal,
            (CalcResult::EmptyArg, CalcResult::EmptyCell) => Ordering::Equal,
            (CalcResult::EmptyArg, CalcResult::EmptyArg) => Ordering::Equal,
            // NOTE: Errors and Ranges are not covered
            (_, _) => Ordering::Greater,
        }
    }
}

impl PartialOrd for CalcResult {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for CalcResult {
    fn eq(&self, other: &Self) -> bool {
        self.cmp(other) == Ordering::Equal
    }
}

impl Eq for CalcResult {}