sheetkit_core/formula/functions/
information.rs1use crate::cell::CellValue;
5use crate::error::Result;
6use crate::formula::ast::Expr;
7use crate::formula::eval::{coerce_to_number, Evaluator};
8use crate::formula::functions::check_arg_count;
9
10pub fn fn_iserr(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
12 check_arg_count("ISERR", args, 1, 1)?;
13 let v = ctx.eval_expr(&args[0])?;
14 let result = match &v {
15 CellValue::Error(e) => e != "#N/A",
16 _ => false,
17 };
18 Ok(CellValue::Bool(result))
19}
20
21pub fn fn_isna(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
23 check_arg_count("ISNA", args, 1, 1)?;
24 let v = ctx.eval_expr(&args[0])?;
25 let result = matches!(&v, CellValue::Error(e) if e == "#N/A");
26 Ok(CellValue::Bool(result))
27}
28
29pub fn fn_islogical(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
31 check_arg_count("ISLOGICAL", args, 1, 1)?;
32 let v = ctx.eval_expr(&args[0])?;
33 Ok(CellValue::Bool(matches!(v, CellValue::Bool(_))))
34}
35
36pub fn fn_iseven(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
38 check_arg_count("ISEVEN", args, 1, 1)?;
39 let v = ctx.eval_expr(&args[0])?;
40 let n = coerce_to_number(&v)?;
41 let int_part = n.trunc() as i64;
42 Ok(CellValue::Bool(int_part % 2 == 0))
43}
44
45pub fn fn_isodd(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
47 check_arg_count("ISODD", args, 1, 1)?;
48 let v = ctx.eval_expr(&args[0])?;
49 let n = coerce_to_number(&v)?;
50 let int_part = n.trunc() as i64;
51 Ok(CellValue::Bool(int_part % 2 != 0))
52}
53
54pub fn fn_type(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
56 check_arg_count("TYPE", args, 1, 1)?;
57 let v = ctx.eval_expr(&args[0])?;
58 let code = match v {
59 CellValue::Number(_) | CellValue::Date(_) | CellValue::Empty => 1.0,
60 CellValue::String(_) | CellValue::RichString(_) => 2.0,
61 CellValue::Bool(_) => 4.0,
62 CellValue::Error(_) => 16.0,
63 CellValue::Formula { .. } => 1.0,
64 };
65 Ok(CellValue::Number(code))
66}
67
68pub fn fn_n(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
70 check_arg_count("N", args, 1, 1)?;
71 let v = ctx.eval_expr(&args[0])?;
72 match v {
73 CellValue::Number(n) => Ok(CellValue::Number(n)),
74 CellValue::Date(n) => Ok(CellValue::Number(n)),
75 CellValue::Bool(b) => Ok(CellValue::Number(if b { 1.0 } else { 0.0 })),
76 CellValue::Error(_) => Ok(v),
77 _ => Ok(CellValue::Number(0.0)),
78 }
79}
80
81pub fn fn_na(args: &[Expr], _ctx: &mut Evaluator) -> Result<CellValue> {
83 check_arg_count("NA", args, 0, 0)?;
84 Ok(CellValue::Error("#N/A".to_string()))
85}
86
87pub fn fn_error_type(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
91 check_arg_count("ERROR.TYPE", args, 1, 1)?;
92 let v = ctx.eval_expr(&args[0])?;
93 match v {
94 CellValue::Error(ref e) => {
95 let code = match e.as_str() {
96 "#NULL!" => 1.0,
97 "#DIV/0!" => 2.0,
98 "#VALUE!" => 3.0,
99 "#REF!" => 4.0,
100 "#NAME?" => 5.0,
101 "#NUM!" => 6.0,
102 "#N/A" => 7.0,
103 _ => return Ok(CellValue::Error("#N/A".to_string())),
104 };
105 Ok(CellValue::Number(code))
106 }
107 _ => Ok(CellValue::Error("#N/A".to_string())),
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use crate::cell::CellValue;
114 use crate::formula::eval::{evaluate, CellSnapshot};
115 use crate::formula::parser::parse_formula;
116
117 fn eval_with_data(formula: &str, data: &[(&str, u32, u32, CellValue)]) -> CellValue {
118 let mut snap = CellSnapshot::new("Sheet1".to_string());
119 for (sheet, col, row, val) in data {
120 snap.set_cell(sheet, *col, *row, val.clone());
121 }
122 let expr = parse_formula(formula).unwrap();
123 evaluate(&expr, &snap).unwrap()
124 }
125
126 fn eval(formula: &str) -> CellValue {
127 eval_with_data(formula, &[])
128 }
129
130 #[test]
133 fn iserr_div_zero() {
134 let data = vec![("Sheet1", 1, 1, CellValue::Error("#DIV/0!".to_string()))];
135 assert_eq!(eval_with_data("ISERR(A1)", &data), CellValue::Bool(true));
136 }
137
138 #[test]
139 fn iserr_na_is_false() {
140 let data = vec![("Sheet1", 1, 1, CellValue::Error("#N/A".to_string()))];
141 assert_eq!(eval_with_data("ISERR(A1)", &data), CellValue::Bool(false));
142 }
143
144 #[test]
145 fn iserr_not_error() {
146 assert_eq!(eval("ISERR(42)"), CellValue::Bool(false));
147 }
148
149 #[test]
152 fn isna_true() {
153 assert_eq!(eval("ISNA(NA())"), CellValue::Bool(true));
154 }
155
156 #[test]
157 fn isna_false_for_other_error() {
158 let data = vec![("Sheet1", 1, 1, CellValue::Error("#DIV/0!".to_string()))];
159 assert_eq!(eval_with_data("ISNA(A1)", &data), CellValue::Bool(false));
160 }
161
162 #[test]
163 fn isna_false_for_number() {
164 assert_eq!(eval("ISNA(1)"), CellValue::Bool(false));
165 }
166
167 #[test]
170 fn islogical_true() {
171 assert_eq!(eval("ISLOGICAL(TRUE)"), CellValue::Bool(true));
172 }
173
174 #[test]
175 fn islogical_false_for_number() {
176 assert_eq!(eval("ISLOGICAL(1)"), CellValue::Bool(false));
177 }
178
179 #[test]
182 fn iseven_true() {
183 assert_eq!(eval("ISEVEN(4)"), CellValue::Bool(true));
184 }
185
186 #[test]
187 fn iseven_false() {
188 assert_eq!(eval("ISEVEN(3)"), CellValue::Bool(false));
189 }
190
191 #[test]
192 fn iseven_zero() {
193 assert_eq!(eval("ISEVEN(0)"), CellValue::Bool(true));
194 }
195
196 #[test]
199 fn isodd_true() {
200 assert_eq!(eval("ISODD(3)"), CellValue::Bool(true));
201 }
202
203 #[test]
204 fn isodd_false() {
205 assert_eq!(eval("ISODD(4)"), CellValue::Bool(false));
206 }
207
208 #[test]
211 fn type_number() {
212 assert_eq!(eval("TYPE(42)"), CellValue::Number(1.0));
213 }
214
215 #[test]
216 fn type_text() {
217 assert_eq!(eval("TYPE(\"hello\")"), CellValue::Number(2.0));
218 }
219
220 #[test]
221 fn type_boolean() {
222 assert_eq!(eval("TYPE(TRUE)"), CellValue::Number(4.0));
223 }
224
225 #[test]
226 fn type_error() {
227 assert_eq!(eval("TYPE(NA())"), CellValue::Number(16.0));
228 }
229
230 #[test]
233 fn n_number() {
234 assert_eq!(eval("N(42)"), CellValue::Number(42.0));
235 }
236
237 #[test]
238 fn n_true() {
239 assert_eq!(eval("N(TRUE)"), CellValue::Number(1.0));
240 }
241
242 #[test]
243 fn n_false() {
244 assert_eq!(eval("N(FALSE)"), CellValue::Number(0.0));
245 }
246
247 #[test]
248 fn n_string() {
249 assert_eq!(eval("N(\"text\")"), CellValue::Number(0.0));
250 }
251
252 #[test]
253 fn n_error() {
254 assert_eq!(eval("N(NA())"), CellValue::Error("#N/A".to_string()));
255 }
256
257 #[test]
260 fn na_returns_error() {
261 assert_eq!(eval("NA()"), CellValue::Error("#N/A".to_string()));
262 }
263
264 #[test]
267 fn error_type_div_zero() {
268 let data = vec![("Sheet1", 1, 1, CellValue::Error("#DIV/0!".to_string()))];
269 assert_eq!(
270 eval_with_data("ERROR.TYPE(A1)", &data),
271 CellValue::Number(2.0)
272 );
273 }
274
275 #[test]
276 fn error_type_na() {
277 assert_eq!(eval("ERROR.TYPE(NA())"), CellValue::Number(7.0));
278 }
279
280 #[test]
281 fn error_type_value_error() {
282 let data = vec![("Sheet1", 1, 1, CellValue::Error("#VALUE!".to_string()))];
283 assert_eq!(
284 eval_with_data("ERROR.TYPE(A1)", &data),
285 CellValue::Number(3.0)
286 );
287 }
288
289 #[test]
290 fn error_type_not_error() {
291 assert_eq!(eval("ERROR.TYPE(42)"), CellValue::Error("#N/A".to_string()));
292 }
293}