ironcalc_base/functions/macros.rs
1#[macro_export]
2macro_rules! single_number_fn {
3 // The macro takes:
4 // 1) A function name to define (e.g. fn_sin)
5 // 2) The operation to apply (e.g. f64::sin)
6 ($fn_name:ident, $op:expr) => {
7 pub(crate) fn $fn_name(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
8 // 1) Check exactly one argument
9 if args.len() != 1 {
10 return CalcResult::new_args_number_error(cell);
11 }
12 // 2) Try to get a "NumberOrArray"
13 match self.get_number_or_array(&args[0], cell) {
14 // -----------------------------------------
15 // Case A: It's a single number
16 // -----------------------------------------
17 Ok(NumberOrArray::Number(f)) => match $op(f) {
18 Ok(x) => CalcResult::Number(x),
19 Err(Error::DIV) => CalcResult::Error {
20 error: Error::DIV,
21 origin: cell,
22 message: "Divide by 0".to_string(),
23 },
24 Err(Error::VALUE) => CalcResult::Error {
25 error: Error::VALUE,
26 origin: cell,
27 message: "Invalid number".to_string(),
28 },
29 Err(e) => CalcResult::Error {
30 error: e,
31 origin: cell,
32 message: "Unknown error".to_string(),
33 },
34 },
35
36 // -----------------------------------------
37 // Case B: It's an array, so apply $op
38 // element-by-element.
39 // -----------------------------------------
40 Ok(NumberOrArray::Array(a)) => {
41 let mut array = Vec::new();
42 for row in a {
43 let mut data_row = Vec::with_capacity(row.len());
44 for value in row {
45 match value {
46 // If Boolean, treat as 0.0 or 1.0
47 ArrayNode::Boolean(b) => {
48 let n = if b { 1.0 } else { 0.0 };
49 match $op(n) {
50 Ok(x) => data_row.push(ArrayNode::Number(x)),
51 Err(Error::DIV) => {
52 data_row.push(ArrayNode::Error(Error::DIV))
53 }
54 Err(Error::VALUE) => {
55 data_row.push(ArrayNode::Error(Error::VALUE))
56 }
57 Err(e) => data_row.push(ArrayNode::Error(e)),
58 }
59 }
60 // If Number, apply directly
61 ArrayNode::Number(n) => match $op(n) {
62 Ok(x) => data_row.push(ArrayNode::Number(x)),
63 Err(Error::DIV) => data_row.push(ArrayNode::Error(Error::DIV)),
64 Err(Error::VALUE) => {
65 data_row.push(ArrayNode::Error(Error::VALUE))
66 }
67 Err(e) => data_row.push(ArrayNode::Error(e)),
68 },
69 // If String, parse to f64 then apply or #VALUE! error
70 ArrayNode::String(s) => {
71 let node = match self.cast_number(&s) {
72 Some(f) => match $op(f) {
73 Ok(x) => ArrayNode::Number(x),
74 Err(Error::DIV) => ArrayNode::Error(Error::DIV),
75 Err(Error::VALUE) => ArrayNode::Error(Error::VALUE),
76 Err(e) => ArrayNode::Error(e),
77 },
78 None => ArrayNode::Error(Error::VALUE),
79 };
80 data_row.push(node);
81 }
82 // If Error, propagate the error
83 e @ ArrayNode::Error(_) => {
84 data_row.push(e);
85 }
86 // Empty cell: treat as 0.0
87 ArrayNode::Empty => match $op(0.0) {
88 Ok(x) => data_row.push(ArrayNode::Number(x)),
89 Err(Error::DIV) => data_row.push(ArrayNode::Error(Error::DIV)),
90 Err(Error::VALUE) => {
91 data_row.push(ArrayNode::Error(Error::VALUE))
92 }
93 Err(e) => data_row.push(ArrayNode::Error(e)),
94 },
95 }
96 }
97 array.push(data_row);
98 }
99 CalcResult::Array(array)
100 }
101
102 // -----------------------------------------
103 // Case C: It's an Error => just return it
104 // -----------------------------------------
105 Err(err_result) => err_result,
106 }
107 }
108 };
109}