formualizer_eval/builtins/text/
len_left_right.rs

1use super::super::utils::ARG_ANY_ONE;
2use crate::args::ArgSchema;
3use crate::function::Function;
4use crate::traits::{ArgumentHandle, FunctionContext};
5use formualizer_common::{ExcelError, LiteralValue};
6use formualizer_macros::func_caps;
7
8#[derive(Debug)]
9pub struct LenFn;
10impl Function for LenFn {
11    func_caps!(PURE);
12    fn name(&self) -> &'static str {
13        "LEN"
14    }
15    fn min_args(&self) -> usize {
16        1
17    }
18    fn arg_schema(&self) -> &'static [ArgSchema] {
19        &ARG_ANY_ONE[..]
20    }
21    fn eval_scalar<'a, 'b>(
22        &self,
23        args: &'a [ArgumentHandle<'a, 'b>],
24        _: &dyn FunctionContext,
25    ) -> Result<LiteralValue, ExcelError> {
26        let v = args[0].value()?;
27        let count = match v.as_ref() {
28            LiteralValue::Text(s) => s.chars().count() as i64,
29            LiteralValue::Empty => 0,
30            LiteralValue::Error(e) => return Ok(LiteralValue::Error(e.clone())),
31            other => other.to_string().chars().count() as i64,
32        };
33        Ok(LiteralValue::Int(count))
34    }
35}
36
37#[derive(Debug)]
38pub struct LeftFn;
39impl Function for LeftFn {
40    func_caps!(PURE);
41    fn name(&self) -> &'static str {
42        "LEFT"
43    }
44    fn min_args(&self) -> usize {
45        1
46    }
47    fn variadic(&self) -> bool {
48        true
49    }
50    fn arg_schema(&self) -> &'static [ArgSchema] {
51        &ARG_ANY_ONE[..]
52    }
53    fn eval_scalar<'a, 'b>(
54        &self,
55        args: &'a [ArgumentHandle<'a, 'b>],
56        _: &dyn FunctionContext,
57    ) -> Result<LiteralValue, ExcelError> {
58        if args.is_empty() || args.len() > 2 {
59            return Ok(LiteralValue::Error(ExcelError::new_value()));
60        }
61        let s_val = args[0].value()?;
62        let s = match s_val.as_ref() {
63            LiteralValue::Text(t) => t.clone(),
64            LiteralValue::Empty => String::new(),
65            LiteralValue::Error(e) => return Ok(LiteralValue::Error(e.clone())),
66            other => other.to_string(),
67        };
68        let n: i64 = if args.len() == 2 {
69            number_like(&args[1])?
70        } else {
71            1
72        };
73        if n < 0 {
74            return Ok(LiteralValue::Error(ExcelError::new_value()));
75        }
76        let chars: Vec<char> = s.chars().collect();
77        let take = (n as usize).min(chars.len());
78        Ok(LiteralValue::Text(chars[..take].iter().collect()))
79    }
80}
81
82#[derive(Debug)]
83pub struct RightFn;
84impl Function for RightFn {
85    func_caps!(PURE);
86    fn name(&self) -> &'static str {
87        "RIGHT"
88    }
89    fn min_args(&self) -> usize {
90        1
91    }
92    fn variadic(&self) -> bool {
93        true
94    }
95    fn arg_schema(&self) -> &'static [ArgSchema] {
96        &ARG_ANY_ONE[..]
97    }
98    fn eval_scalar<'a, 'b>(
99        &self,
100        args: &'a [ArgumentHandle<'a, 'b>],
101        _: &dyn FunctionContext,
102    ) -> Result<LiteralValue, ExcelError> {
103        if args.is_empty() || args.len() > 2 {
104            return Ok(LiteralValue::Error(ExcelError::new_value()));
105        }
106        let s_val = args[0].value()?;
107        let s = match s_val.as_ref() {
108            LiteralValue::Text(t) => t.clone(),
109            LiteralValue::Empty => String::new(),
110            LiteralValue::Error(e) => return Ok(LiteralValue::Error(e.clone())),
111            other => other.to_string(),
112        };
113        let n: i64 = if args.len() == 2 {
114            number_like(&args[1])?
115        } else {
116            1
117        };
118        if n < 0 {
119            return Ok(LiteralValue::Error(ExcelError::new_value()));
120        }
121        let chars: Vec<char> = s.chars().collect();
122        let len = chars.len();
123        let start = len.saturating_sub(n as usize);
124        Ok(LiteralValue::Text(chars[start..].iter().collect()))
125    }
126}
127
128fn number_like<'a, 'b>(arg: &ArgumentHandle<'a, 'b>) -> Result<i64, ExcelError> {
129    let v = arg.value()?;
130    Ok(match v.as_ref() {
131        LiteralValue::Int(i) => *i,
132        LiteralValue::Number(f) => *f as i64,
133        LiteralValue::Empty => 0,
134        LiteralValue::Text(t) => t.parse::<i64>().unwrap_or(0),
135        LiteralValue::Boolean(b) => {
136            if *b {
137                1
138            } else {
139                0
140            }
141        }
142        LiteralValue::Error(e) => return Err(e.clone()),
143        other => other.to_string().parse::<i64>().unwrap_or(0),
144    })
145}
146
147pub fn register_builtins() {
148    use std::sync::Arc;
149    crate::function_registry::register_function(Arc::new(LenFn));
150    crate::function_registry::register_function(Arc::new(LeftFn));
151    crate::function_registry::register_function(Arc::new(RightFn));
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157    use crate::test_workbook::TestWorkbook;
158    use crate::traits::ArgumentHandle;
159    use formualizer_common::LiteralValue;
160    use formualizer_parse::parser::{ASTNode, ASTNodeType};
161    fn lit(v: LiteralValue) -> ASTNode {
162        ASTNode::new(ASTNodeType::Literal(v), None)
163    }
164    #[test]
165    fn len_basic() {
166        let wb = TestWorkbook::new().with_function(std::sync::Arc::new(LenFn));
167        let ctx = wb.interpreter();
168        let f = ctx.context.get_function("", "LEN").unwrap();
169        let s = lit(LiteralValue::Text("abc".into()));
170        let out = f
171            .dispatch(
172                &[ArgumentHandle::new(&s, &ctx)],
173                &ctx.function_context(None),
174            )
175            .unwrap();
176        assert_eq!(out, LiteralValue::Int(3));
177    }
178    #[test]
179    fn left_right() {
180        let wb = TestWorkbook::new()
181            .with_function(std::sync::Arc::new(LeftFn))
182            .with_function(std::sync::Arc::new(RightFn));
183        let ctx = wb.interpreter();
184        let l = ctx.context.get_function("", "LEFT").unwrap();
185        let r = ctx.context.get_function("", "RIGHT").unwrap();
186        let s = lit(LiteralValue::Text("hello".into()));
187        let n = lit(LiteralValue::Int(2));
188        assert_eq!(
189            l.dispatch(
190                &[ArgumentHandle::new(&s, &ctx), ArgumentHandle::new(&n, &ctx)],
191                &ctx.function_context(None)
192            )
193            .unwrap(),
194            LiteralValue::Text("he".into())
195        );
196        assert_eq!(
197            r.dispatch(
198                &[ArgumentHandle::new(&s, &ctx), ArgumentHandle::new(&n, &ctx)],
199                &ctx.function_context(None)
200            )
201            .unwrap(),
202            LiteralValue::Text("lo".into())
203        );
204    }
205}