-
Notifications
You must be signed in to change notification settings - Fork 0
[JULES] Refactor text stdlib to reduce duplication #374
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,6 @@ | ||
| use super::helpers::{check_arg_count, expect_number, expect_text}; | ||
| use super::helpers::{ | ||
| binary_text_predicate, check_arg_count, expect_number, expect_text, unary_text_op, | ||
| }; | ||
| use crate::interpreter::environment::Environment; | ||
| use crate::interpreter::error::RuntimeError; | ||
| use crate::interpreter::value::Value; | ||
|
|
@@ -74,19 +76,11 @@ fn parse_key_value_pairs(input: &str, delimiter: char) -> std::collections::Hash | |
| // which handles both text and lists | ||
|
|
||
| pub fn native_touppercase(args: Vec<Value>) -> Result<Value, RuntimeError> { | ||
| check_arg_count("touppercase", &args, 1)?; | ||
|
|
||
| let text = expect_text(&args[0])?; | ||
| let uppercase = text.to_uppercase(); | ||
| Ok(Value::Text(Arc::from(uppercase))) | ||
| unary_text_op("touppercase", args, |s| s.to_uppercase()) | ||
| } | ||
|
|
||
| pub fn native_tolowercase(args: Vec<Value>) -> Result<Value, RuntimeError> { | ||
| check_arg_count("tolowercase", &args, 1)?; | ||
|
|
||
| let text = expect_text(&args[0])?; | ||
| let lowercase = text.to_lowercase(); | ||
| Ok(Value::Text(Arc::from(lowercase))) | ||
| unary_text_op("tolowercase", args, |s| s.to_lowercase()) | ||
| } | ||
|
|
||
| pub fn native_substring(args: Vec<Value>) -> Result<Value, RuntimeError> { | ||
|
|
@@ -135,29 +129,15 @@ pub fn native_string_split(args: Vec<Value>) -> Result<Value, RuntimeError> { | |
| } | ||
|
|
||
| pub fn native_trim(args: Vec<Value>) -> Result<Value, RuntimeError> { | ||
| 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| Arc::from(s.trim())) | ||
|
||
| } | ||
|
|
||
| pub fn native_starts_with(args: Vec<Value>) -> Result<Value, RuntimeError> { | ||
| 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, |text, prefix| text.starts_with(prefix)) | ||
| } | ||
|
|
||
| pub fn native_ends_with(args: Vec<Value>) -> Result<Value, RuntimeError> { | ||
| 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, |text, suffix| text.ends_with(suffix)) | ||
| } | ||
|
|
||
| /// Parse query string into WFL object | ||
|
|
@@ -249,8 +229,17 @@ fn validated_pad_width(raw: f64) -> Result<usize, RuntimeError> { | |
| Ok((raw as usize).min(MAX_PAD_WIDTH)) | ||
| } | ||
|
|
||
| pub fn native_padleft(args: Vec<Value>) -> Result<Value, RuntimeError> { | ||
| check_arg_count("padleft", &args, 2)?; | ||
| enum PadDirection { | ||
| Left, | ||
| Right, | ||
| } | ||
|
|
||
| fn perform_pad(args: Vec<Value>, direction: PadDirection) -> Result<Value, RuntimeError> { | ||
| let func_name = match direction { | ||
| PadDirection::Left => "padleft", | ||
| PadDirection::Right => "padright", | ||
| }; | ||
| check_arg_count(func_name, &args, 2)?; | ||
|
|
||
| let text = expect_text(&args[0])?; | ||
| let width = validated_pad_width(expect_number(&args[1])?)?; | ||
|
|
@@ -259,45 +248,40 @@ pub fn native_padleft(args: Vec<Value>) -> Result<Value, RuntimeError> { | |
| Ok(Value::Text(Arc::clone(&text))) | ||
| } else { | ||
| let padding = " ".repeat(width - len); | ||
| Ok(Value::Text(Arc::from(format!("{}{}", padding, text)))) | ||
| let result = match direction { | ||
| PadDirection::Left => format!("{}{}", padding, text), | ||
| PadDirection::Right => format!("{}{}", text, padding), | ||
| }; | ||
| Ok(Value::Text(Arc::from(result))) | ||
| } | ||
| } | ||
|
|
||
| pub fn native_padright(args: Vec<Value>) -> Result<Value, RuntimeError> { | ||
| check_arg_count("padright", &args, 2)?; | ||
| pub fn native_padleft(args: Vec<Value>) -> Result<Value, RuntimeError> { | ||
| perform_pad(args, PadDirection::Left) | ||
| } | ||
|
|
||
| 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<Value>) -> Result<Value, RuntimeError> { | ||
| perform_pad(args, PadDirection::Right) | ||
| } | ||
|
|
||
| pub fn native_capitalize(args: Vec<Value>) -> Result<Value, RuntimeError> { | ||
| 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, |text| { | ||
| let mut chars = text.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<Value>) -> Result<Value, RuntimeError> { | ||
| 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, |text| { | ||
| let reversed: String = text.chars().rev().collect(); | ||
| reversed | ||
| }) | ||
| } | ||
|
|
||
| pub fn register_text(env: &mut Environment) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test coverage for
binary_text_predicateis incomplete. While there are tests for successful execution and incorrect argument count, there's no test verifying that the function correctly rejects arguments of the wrong type (e.g., passing a Number instead of Text). Consider adding a test similar totest_unary_text_op_wrong_typeto ensure type validation works correctly.