From 173c1accc1e81664b52aa162e428f981429e9603 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 13 Feb 2026 10:06:30 +0000 Subject: [PATCH] Refactor text stdlib to use generic helper functions - Introduced `unary_text_op` for single-argument text transformations. - Introduced `binary_text_predicate` for two-argument text predicates. - Introduced `pad_helper` for text padding operations. - Refactored `native_touppercase`, `native_tolowercase`, `native_trim`, `native_starts_with`, `native_ends_with`, `native_padleft`, `native_padright`, `native_capitalize`, and `native_reverse_text` to use these helpers. - Reduced code duplication and improved maintainability. Co-authored-by: logbie <1138960+logbie@users.noreply.github.com> --- src/stdlib/text.rs | 102 ++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 56 deletions(-) diff --git a/src/stdlib/text.rs b/src/stdlib/text.rs index b91c428b..f5ffafcc 100644 --- a/src/stdlib/text.rs +++ b/src/stdlib/text.rs @@ -73,20 +73,31 @@ fn parse_key_value_pairs(input: &str, delimiter: char) -> std::collections::Hash // Note: The length function is now provided by the list module // which handles both text and lists -pub fn native_touppercase(args: Vec) -> Result { - check_arg_count("touppercase", &args, 1)?; +fn unary_text_op(func_name: &str, args: &[Value], op: F) -> Result +where + F: FnOnce(&str) -> String, +{ + check_arg_count(func_name, args, 1)?; + let text = expect_text(&args[0])?; + Ok(Value::Text(Arc::from(op(&text)))) +} +fn binary_text_predicate(func_name: &str, args: &[Value], op: F) -> Result +where + F: FnOnce(&str, &str) -> bool, +{ + check_arg_count(func_name, args, 2)?; let text = expect_text(&args[0])?; - let uppercase = text.to_uppercase(); - Ok(Value::Text(Arc::from(uppercase))) + let other = expect_text(&args[1])?; + Ok(Value::Bool(op(&text, &other))) } -pub fn native_tolowercase(args: Vec) -> Result { - check_arg_count("tolowercase", &args, 1)?; +pub fn native_touppercase(args: Vec) -> Result { + unary_text_op("touppercase", &args, |s| s.to_uppercase()) +} - let text = expect_text(&args[0])?; - let lowercase = text.to_lowercase(); - Ok(Value::Text(Arc::from(lowercase))) +pub fn native_tolowercase(args: Vec) -> Result { + unary_text_op("tolowercase", &args, |s| s.to_lowercase()) } pub fn native_substring(args: Vec) -> Result { @@ -135,29 +146,15 @@ pub fn native_string_split(args: Vec) -> Result { } pub fn native_trim(args: Vec) -> Result { - check_arg_count("trim", &args, 1)?; - - let text = expect_text(&args[0])?; - let trimmed = text.trim(); - Ok(Value::Text(Arc::from(trimmed))) + unary_text_op("trim", &args, |s| s.trim().to_string()) } pub fn native_starts_with(args: Vec) -> Result { - check_arg_count("starts_with", &args, 2)?; - - let text = expect_text(&args[0])?; - let prefix = expect_text(&args[1])?; - let result = text.starts_with(prefix.as_ref()); - Ok(Value::Bool(result)) + binary_text_predicate("starts_with", &args, |s, p| s.starts_with(p)) } pub fn native_ends_with(args: Vec) -> Result { - check_arg_count("ends_with", &args, 2)?; - - let text = expect_text(&args[0])?; - let suffix = expect_text(&args[1])?; - let result = text.ends_with(suffix.as_ref()); - Ok(Value::Bool(result)) + binary_text_predicate("ends_with", &args, |s, p| s.ends_with(p)) } /// Parse query string into WFL object @@ -249,8 +246,8 @@ fn validated_pad_width(raw: f64) -> Result { Ok((raw as usize).min(MAX_PAD_WIDTH)) } -pub fn native_padleft(args: Vec) -> Result { - check_arg_count("padleft", &args, 2)?; +fn pad_helper(func_name: &str, args: &[Value], is_left: bool) -> Result { + check_arg_count(func_name, args, 2)?; let text = expect_text(&args[0])?; let width = validated_pad_width(expect_number(&args[1])?)?; @@ -259,45 +256,38 @@ pub fn native_padleft(args: Vec) -> Result { Ok(Value::Text(Arc::clone(&text))) } else { let padding = " ".repeat(width - len); - Ok(Value::Text(Arc::from(format!("{}{}", padding, text)))) + let result = if is_left { + format!("{}{}", padding, text) + } else { + format!("{}{}", text, padding) + }; + Ok(Value::Text(Arc::from(result))) } } -pub fn native_padright(args: Vec) -> Result { - check_arg_count("padright", &args, 2)?; +pub fn native_padleft(args: Vec) -> Result { + pad_helper("padleft", &args, true) +} - let text = expect_text(&args[0])?; - let width = validated_pad_width(expect_number(&args[1])?)?; - let len = text.chars().count(); - if len >= width { - Ok(Value::Text(Arc::clone(&text))) - } else { - let padding = " ".repeat(width - len); - Ok(Value::Text(Arc::from(format!("{}{}", text, padding)))) - } +pub fn native_padright(args: Vec) -> Result { + pad_helper("padright", &args, false) } pub fn native_capitalize(args: Vec) -> Result { - check_arg_count("capitalize", &args, 1)?; - - let text = expect_text(&args[0])?; - let mut chars = text.chars(); - let result = match chars.next() { - Some(c) => { - let upper: String = c.to_uppercase().collect(); - format!("{}{}", upper, chars.as_str()) + unary_text_op("capitalize", &args, |s| { + let mut chars = s.chars(); + match chars.next() { + Some(c) => { + let upper: String = c.to_uppercase().collect(); + format!("{}{}", upper, chars.as_str()) + } + None => String::new(), } - None => String::new(), - }; - Ok(Value::Text(Arc::from(result))) + }) } pub fn native_reverse_text(args: Vec) -> Result { - check_arg_count("reverse", &args, 1)?; - - let text = expect_text(&args[0])?; - let reversed: String = text.chars().rev().collect(); - Ok(Value::Text(Arc::from(reversed))) + unary_text_op("reverse", &args, |s| s.chars().rev().collect()) } pub fn register_text(env: &mut Environment) {