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, ExcelErrorKind, LiteralValue};
6use formualizer_macros::func_caps;
7
8fn scalar_like_value(arg: &ArgumentHandle<'_, '_>) -> Result<LiteralValue, ExcelError> {
9 Ok(match arg.value()? {
10 crate::traits::CalcValue::Scalar(v) => v,
11 crate::traits::CalcValue::Range(rv) => rv.get_cell(0, 0),
12 crate::traits::CalcValue::Callable(_) => LiteralValue::Error(
13 ExcelError::new(ExcelErrorKind::Calc).with_message("LAMBDA value must be invoked"),
14 ),
15 })
16}
17
18#[derive(Debug)]
19pub struct LenFn;
20impl Function for LenFn {
62 func_caps!(PURE);
63 fn name(&self) -> &'static str {
64 "LEN"
65 }
66 fn min_args(&self) -> usize {
67 1
68 }
69 fn arg_schema(&self) -> &'static [ArgSchema] {
70 &ARG_ANY_ONE[..]
71 }
72 fn eval<'a, 'b, 'c>(
73 &self,
74 args: &'c [ArgumentHandle<'a, 'b>],
75 _: &dyn FunctionContext<'b>,
76 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
77 let v = scalar_like_value(&args[0])?;
78 let count = match v {
79 LiteralValue::Text(s) => s.chars().count() as i64,
80 LiteralValue::Empty => 0,
81 LiteralValue::Error(e) => {
82 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
83 }
84 other => other.to_string().chars().count() as i64,
85 };
86 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Int(count)))
87 }
88}
89
90#[derive(Debug)]
91pub struct LeftFn;
92impl Function for LeftFn {
134 func_caps!(PURE);
135 fn name(&self) -> &'static str {
136 "LEFT"
137 }
138 fn min_args(&self) -> usize {
139 1
140 }
141 fn variadic(&self) -> bool {
142 true
143 }
144 fn arg_schema(&self) -> &'static [ArgSchema] {
145 &ARG_ANY_ONE[..]
146 }
147 fn eval<'a, 'b, 'c>(
148 &self,
149 args: &'c [ArgumentHandle<'a, 'b>],
150 _: &dyn FunctionContext<'b>,
151 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
152 if args.is_empty() || args.len() > 2 {
153 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
154 ExcelError::new_value(),
155 )));
156 }
157 let s_val = scalar_like_value(&args[0])?;
158 let s = match s_val {
159 LiteralValue::Text(t) => t,
160 LiteralValue::Empty => String::new(),
161 LiteralValue::Error(e) => {
162 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
163 }
164 other => other.to_string(),
165 };
166 let n: i64 = if args.len() == 2 {
167 number_like(&args[1])?
168 } else {
169 1
170 };
171 if n < 0 {
172 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
173 ExcelError::new_value(),
174 )));
175 }
176 let chars: Vec<char> = s.chars().collect();
177 let take = (n as usize).min(chars.len());
178 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(
179 chars[..take].iter().collect(),
180 )))
181 }
182}
183
184#[derive(Debug)]
185pub struct RightFn;
186impl Function for RightFn {
228 func_caps!(PURE);
229 fn name(&self) -> &'static str {
230 "RIGHT"
231 }
232 fn min_args(&self) -> usize {
233 1
234 }
235 fn variadic(&self) -> bool {
236 true
237 }
238 fn arg_schema(&self) -> &'static [ArgSchema] {
239 &ARG_ANY_ONE[..]
240 }
241 fn eval<'a, 'b, 'c>(
242 &self,
243 args: &'c [ArgumentHandle<'a, 'b>],
244 _: &dyn FunctionContext<'b>,
245 ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
246 if args.is_empty() || args.len() > 2 {
247 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
248 ExcelError::new_value(),
249 )));
250 }
251 let s_val = scalar_like_value(&args[0])?;
252 let s = match s_val {
253 LiteralValue::Text(t) => t,
254 LiteralValue::Empty => String::new(),
255 LiteralValue::Error(e) => {
256 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
257 }
258 other => other.to_string(),
259 };
260 let n: i64 = if args.len() == 2 {
261 number_like(&args[1])?
262 } else {
263 1
264 };
265 if n < 0 {
266 return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
267 ExcelError::new_value(),
268 )));
269 }
270 let chars: Vec<char> = s.chars().collect();
271 let len = chars.len();
272 let start = len.saturating_sub(n as usize);
273 Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(
274 chars[start..].iter().collect(),
275 )))
276 }
277}
278
279fn number_like<'a, 'b>(arg: &ArgumentHandle<'a, 'b>) -> Result<i64, ExcelError> {
280 let v = scalar_like_value(arg)?;
281 Ok(match v {
282 LiteralValue::Int(i) => i,
283 LiteralValue::Number(f) => f as i64,
284 LiteralValue::Empty => 0,
285 LiteralValue::Text(t) => t.parse::<i64>().unwrap_or(0),
286 LiteralValue::Boolean(b) => {
287 if b {
288 1
289 } else {
290 0
291 }
292 }
293 LiteralValue::Error(e) => return Err(e),
294 other => other.to_string().parse::<i64>().unwrap_or(0),
295 })
296}
297
298pub fn register_builtins() {
299 use std::sync::Arc;
300 crate::function_registry::register_function(Arc::new(LenFn));
301 crate::function_registry::register_function(Arc::new(LeftFn));
302 crate::function_registry::register_function(Arc::new(RightFn));
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308 use crate::test_workbook::TestWorkbook;
309 use crate::traits::ArgumentHandle;
310 use formualizer_common::LiteralValue;
311 use formualizer_parse::parser::{ASTNode, ASTNodeType};
312 fn lit(v: LiteralValue) -> ASTNode {
313 ASTNode::new(ASTNodeType::Literal(v), None)
314 }
315 #[test]
316 fn len_basic() {
317 let wb = TestWorkbook::new().with_function(std::sync::Arc::new(LenFn));
318 let ctx = wb.interpreter();
319 let f = ctx.context.get_function("", "LEN").unwrap();
320 let s = lit(LiteralValue::Text("abc".into()));
321 let out = f
322 .dispatch(
323 &[ArgumentHandle::new(&s, &ctx)],
324 &ctx.function_context(None),
325 )
326 .unwrap();
327 assert_eq!(out.into_literal(), LiteralValue::Int(3));
328 }
329 #[test]
330 fn left_right() {
331 let wb = TestWorkbook::new()
332 .with_function(std::sync::Arc::new(LeftFn))
333 .with_function(std::sync::Arc::new(RightFn));
334 let ctx = wb.interpreter();
335 let l = ctx.context.get_function("", "LEFT").unwrap();
336 let r = ctx.context.get_function("", "RIGHT").unwrap();
337 let s = lit(LiteralValue::Text("hello".into()));
338 let n = lit(LiteralValue::Int(2));
339 assert_eq!(
340 l.dispatch(
341 &[ArgumentHandle::new(&s, &ctx), ArgumentHandle::new(&n, &ctx)],
342 &ctx.function_context(None)
343 )
344 .unwrap()
345 .into_literal(),
346 LiteralValue::Text("he".into())
347 );
348 assert_eq!(
349 r.dispatch(
350 &[ArgumentHandle::new(&s, &ctx), ArgumentHandle::new(&n, &ctx)],
351 &ctx.function_context(None)
352 )
353 .unwrap()
354 .into_literal(),
355 LiteralValue::Text("lo".into())
356 );
357 }
358}