logisheets_controller/calc_engine/calculator/funcs/
utils.rs

1use logisheets_parser::ast;
2
3use super::{CalcValue, Value};
4
5pub enum ConditionResult {
6    True,
7    False,
8    Error(ast::Error),
9}
10
11pub fn get_condition_result(calc_value: CalcValue) -> ConditionResult {
12    match calc_value {
13        CalcValue::Scalar(s) => match s {
14            Value::Blank => ConditionResult::False,
15            Value::Number(num) => {
16                if num.abs() < 1e-10 {
17                    ConditionResult::False
18                } else {
19                    ConditionResult::True
20                }
21            }
22            Value::Text(_) => ConditionResult::Error(ast::Error::Value),
23            Value::Boolean(b) => {
24                if b {
25                    ConditionResult::True
26                } else {
27                    ConditionResult::False
28                }
29            }
30            Value::Error(e) => ConditionResult::Error(e),
31        },
32        CalcValue::Union(_) => ConditionResult::Error(ast::Error::Value),
33        // WPS neither support this.
34        CalcValue::Range(_) => ConditionResult::Error(ast::Error::Value),
35        CalcValue::Cube(_) => ConditionResult::Error(ast::Error::Ref),
36    }
37}
38
39pub fn convert_f64(value: Value) -> Result<f64, ast::Error> {
40    match value {
41        Value::Blank => Ok(0_f64),
42        Value::Number(n) => Ok(n),
43        Value::Text(t) => match t.parse::<f64>() {
44            Ok(n) => Ok(n),
45            Err(_) => Err(ast::Error::Value),
46        },
47        Value::Boolean(b) => {
48            if b {
49                Ok(1.)
50            } else {
51                Ok(0.)
52            }
53        }
54        Value::Error(e) => Err(e),
55    }
56}
57
58pub fn is_na_error(value: &CalcValue) -> bool {
59    match value {
60        CalcValue::Scalar(s) => match s {
61            Value::Error(e) => *e == ast::Error::Na,
62            _ => false,
63        },
64        CalcValue::Range(_) => false,
65        CalcValue::Cube(_) => true,
66        CalcValue::Union(_) => false,
67    }
68}
69
70pub fn is_error(value: &CalcValue) -> bool {
71    match value {
72        CalcValue::Scalar(s) => match s {
73            Value::Error(_) => true,
74            _ => false,
75        },
76        CalcValue::Range(_) => false,
77        CalcValue::Cube(_) => true,
78        CalcValue::Union(_) => false,
79    }
80}
81
82fn get_f64(v: Value) -> Result<Option<f64>, ast::Error> {
83    match v {
84        Value::Number(f) => Ok(Some(f)),
85        Value::Boolean(b) => {
86            if b {
87                Ok(Some(1.))
88            } else {
89                Ok(Some(0.))
90            }
91        }
92        Value::Error(e) => Err(e),
93        _ => Ok(None),
94    }
95}
96
97pub fn get_nums_from_value(value: CalcValue) -> Result<Vec<f64>, ast::Error> {
98    match value {
99        CalcValue::Scalar(v) => match get_f64(v) {
100            Ok(num) => {
101                if let Some(f) = num {
102                    Ok(vec![f])
103                } else {
104                    Ok(vec![])
105                }
106            }
107            Err(e) => Err(e),
108        },
109        CalcValue::Range(r) => r.into_iter().fold(Ok(vec![]), |prev, curr| {
110            if prev.is_err() {
111                return prev;
112            }
113            let num = get_f64(curr);
114            match num {
115                Ok(f) => {
116                    if let Some(n) = f {
117                        let mut v = prev.unwrap();
118                        v.push(n);
119                        Ok(v)
120                    } else {
121                        prev
122                    }
123                }
124                Err(e) => Err(e),
125            }
126        }),
127        CalcValue::Cube(c) => c.into_iter().fold(Ok(vec![]), |prev, curr| {
128            if prev.is_err() {
129                return prev;
130            }
131            let num = get_f64(curr);
132            match num {
133                Ok(f) => {
134                    if let Some(n) = f {
135                        let mut v = prev.unwrap();
136                        v.push(n);
137                        Ok(v)
138                    } else {
139                        prev
140                    }
141                }
142                Err(e) => Err(e),
143            }
144        }),
145        CalcValue::Union(_) => Err(ast::Error::Unspecified),
146    }
147}
148
149#[cfg(test)]
150pub mod tests_utils {
151    use logisheets_base::async_func::{AsyncCalcResult, AsyncFuncCommitTrait, Task};
152    use logisheets_base::get_curr_addr::GetCurrAddrTrait;
153    use logisheets_base::set_curr_cell::SetCurrCellTrait;
154    use logisheets_base::SheetId;
155    use logisheets_parser::ast;
156
157    use crate::calc_engine::calculator::calc_vertex::{CalcValue, CalcVertex};
158    use crate::calc_engine::connector::Connector;
159    use crate::errors::Result;
160    use crate::CellId;
161
162    pub struct TestFetcher {}
163    impl AsyncFuncCommitTrait for TestFetcher {
164        fn query_or_commit_task(
165            &mut self,
166            _sheet_id: logisheets_base::SheetId,
167            _cell_id: logisheets_base::CellId,
168            _task: Task,
169        ) -> Option<AsyncCalcResult> {
170            None
171        }
172    }
173
174    impl GetCurrAddrTrait for TestFetcher {
175        fn get_curr_addr(&self) -> logisheets_base::Addr {
176            unreachable!()
177        }
178    }
179
180    impl SetCurrCellTrait for TestFetcher {
181        fn set_curr_cell(
182            &mut self,
183            _active_sheet: logisheets_base::SheetId,
184            _addr: logisheets_base::Addr,
185        ) {
186            unreachable!()
187        }
188    }
189
190    impl Connector for TestFetcher {
191        fn convert(&mut self, _: &ast::CellReference) -> CalcVertex {
192            unreachable!()
193        }
194
195        fn get_calc_value(&mut self, vertex: CalcVertex) -> CalcValue {
196            match vertex {
197                CalcVertex::Value(v) => v,
198                CalcVertex::Reference(_) => panic!(),
199                CalcVertex::Union(_) => todo!(),
200            }
201        }
202
203        fn get_text(&self, _: &logisheets_base::TextId) -> Result<String> {
204            todo!()
205        }
206
207        fn get_func_name(&self, _: &logisheets_base::FuncId) -> Result<String> {
208            todo!()
209        }
210
211        fn get_cell_idx(
212            &self,
213            _sheet_id: logisheets_base::SheetId,
214            _cell_id: &logisheets_base::CellId,
215        ) -> Result<(usize, usize)> {
216            Ok((0, 0))
217        }
218
219        fn get_cell_id(
220            &self,
221            _sheet_id: logisheets_base::SheetId,
222            _row: usize,
223            _col: usize,
224        ) -> Result<CellId> {
225            unreachable!()
226        }
227
228        fn commit_calc_values(&mut self, _vertex: (SheetId, CellId), _result: CalcValue) {
229            unreachable!()
230        }
231
232        fn is_async_func(&self, _func_name: &str) -> bool {
233            false
234        }
235
236        fn get_range(
237            &self,
238            _sheet_id: &logisheets_base::SheetId,
239            _range: &logisheets_base::RangeId,
240        ) -> Option<logisheets_base::Range> {
241            unreachable!()
242        }
243
244        fn get_sheet_id_by_name(&self, _name: &str) -> Result<SheetId> {
245            unreachable!()
246        }
247
248        fn set_curr_as_dirty(&mut self) -> Result<()> {
249            unreachable!()
250        }
251
252        fn get_active_sheet(&self) -> SheetId {
253            1
254        }
255    }
256}