Skip to main content

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}