use super::{FindFn, LeftFn, LenFn, MidFn, ReplaceFn, RightFn, SearchFn};
use crate::args::ArgSchema;
use crate::builtins::utils::ARG_ANY_ONE;
use crate::function::Function;
use crate::traits::{ArgumentHandle, FunctionContext};
use formualizer_common::ExcelError;
use formualizer_macros::func_caps;
#[derive(Debug)]
pub struct FindBFn;
impl Function for FindBFn {
func_caps!(PURE);
fn name(&self) -> &'static str {
"FINDB"
}
fn min_args(&self) -> usize {
2
}
fn variadic(&self) -> bool {
true
}
fn arg_schema(&self) -> &'static [ArgSchema] {
&ARG_ANY_ONE[..]
}
fn eval<'a, 'b, 'c>(
&self,
args: &'c [ArgumentHandle<'a, 'b>],
ctx: &dyn FunctionContext<'b>,
) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
FindFn.eval(args, ctx)
}
}
#[derive(Debug)]
pub struct LeftBFn;
impl Function for LeftBFn {
func_caps!(PURE);
fn name(&self) -> &'static str {
"LEFTB"
}
fn min_args(&self) -> usize {
1
}
fn variadic(&self) -> bool {
true
}
fn arg_schema(&self) -> &'static [ArgSchema] {
&ARG_ANY_ONE[..]
}
fn eval<'a, 'b, 'c>(
&self,
args: &'c [ArgumentHandle<'a, 'b>],
ctx: &dyn FunctionContext<'b>,
) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
LeftFn.eval(args, ctx)
}
}
#[derive(Debug)]
pub struct LenBFn;
impl Function for LenBFn {
func_caps!(PURE);
fn name(&self) -> &'static str {
"LENB"
}
fn min_args(&self) -> usize {
1
}
fn arg_schema(&self) -> &'static [ArgSchema] {
&ARG_ANY_ONE[..]
}
fn eval<'a, 'b, 'c>(
&self,
args: &'c [ArgumentHandle<'a, 'b>],
ctx: &dyn FunctionContext<'b>,
) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
LenFn.eval(args, ctx)
}
}
#[derive(Debug)]
pub struct MidBFn;
impl Function for MidBFn {
func_caps!(PURE);
fn name(&self) -> &'static str {
"MIDB"
}
fn min_args(&self) -> usize {
3
}
fn arg_schema(&self) -> &'static [ArgSchema] {
&ARG_ANY_ONE[..]
}
fn eval<'a, 'b, 'c>(
&self,
args: &'c [ArgumentHandle<'a, 'b>],
ctx: &dyn FunctionContext<'b>,
) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
MidFn.eval(args, ctx)
}
}
#[derive(Debug)]
pub struct ReplaceBFn;
impl Function for ReplaceBFn {
func_caps!(PURE);
fn name(&self) -> &'static str {
"REPLACEB"
}
fn min_args(&self) -> usize {
4
}
fn arg_schema(&self) -> &'static [ArgSchema] {
&ARG_ANY_ONE[..]
}
fn eval<'a, 'b, 'c>(
&self,
args: &'c [ArgumentHandle<'a, 'b>],
ctx: &dyn FunctionContext<'b>,
) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
ReplaceFn.eval(args, ctx)
}
}
#[derive(Debug)]
pub struct RightBFn;
impl Function for RightBFn {
func_caps!(PURE);
fn name(&self) -> &'static str {
"RIGHTB"
}
fn min_args(&self) -> usize {
1
}
fn variadic(&self) -> bool {
true
}
fn arg_schema(&self) -> &'static [ArgSchema] {
&ARG_ANY_ONE[..]
}
fn eval<'a, 'b, 'c>(
&self,
args: &'c [ArgumentHandle<'a, 'b>],
ctx: &dyn FunctionContext<'b>,
) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
RightFn.eval(args, ctx)
}
}
#[derive(Debug)]
pub struct SearchBFn;
impl Function for SearchBFn {
func_caps!(PURE);
fn name(&self) -> &'static str {
"SEARCHB"
}
fn min_args(&self) -> usize {
2
}
fn variadic(&self) -> bool {
true
}
fn arg_schema(&self) -> &'static [ArgSchema] {
&ARG_ANY_ONE[..]
}
fn eval<'a, 'b, 'c>(
&self,
args: &'c [ArgumentHandle<'a, 'b>],
ctx: &dyn FunctionContext<'b>,
) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
SearchFn.eval(args, ctx)
}
}
pub fn register_builtins() {
use crate::function_registry::register_function;
use std::sync::Arc;
register_function(Arc::new(FindBFn));
register_function(Arc::new(LeftBFn));
register_function(Arc::new(LenBFn));
register_function(Arc::new(MidBFn));
register_function(Arc::new(ReplaceBFn));
register_function(Arc::new(RightBFn));
register_function(Arc::new(SearchBFn));
}
#[cfg(test)]
mod tests {
use crate::test_workbook::TestWorkbook;
use formualizer_common::LiteralValue;
use formualizer_parse::parser::parse;
fn eval(formula: &str) -> LiteralValue {
let wb = TestWorkbook::new()
.with_function(std::sync::Arc::new(super::FindBFn))
.with_function(std::sync::Arc::new(super::LeftBFn))
.with_function(std::sync::Arc::new(super::LenBFn))
.with_function(std::sync::Arc::new(super::MidBFn))
.with_function(std::sync::Arc::new(super::ReplaceBFn))
.with_function(std::sync::Arc::new(super::RightBFn))
.with_function(std::sync::Arc::new(super::SearchBFn));
let interp = wb.interpreter();
let ast = parse(formula).expect("parse");
interp.evaluate_ast(&ast).expect("eval").into_literal()
}
#[test]
fn byte_functions_delegate_in_non_dbcs_locale() {
assert_eq!(eval("=LENB(\"éx\")"), LiteralValue::Int(2));
assert_eq!(eval("=LEFTB(\"hello\",2)"), LiteralValue::Text("he".into()));
assert_eq!(
eval("=RIGHTB(\"hello\",2)"),
LiteralValue::Text("lo".into())
);
assert_eq!(
eval("=MIDB(\"abcdef\",2,3)"),
LiteralValue::Text("bcd".into())
);
assert_eq!(
eval("=REPLACEB(\"abcdef\",3,2,\"ZZ\")"),
LiteralValue::Text("abZZef".into())
);
assert_eq!(eval("=FINDB(\"CD\",\"abcDEFCD\")"), LiteralValue::Int(7));
assert_eq!(eval("=SEARCHB(\"d?f\",\"abcDEF\")"), LiteralValue::Int(4));
}
}