formualizer_eval/builtins/text/
len_left_right.rs1use 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}