gent/interpreter/
string_methods.rs

1//! String method implementations for GENT
2//!
3//! This module provides built-in methods for string values,
4//! including length, trim, split, contains, and more.
5
6use crate::errors::{GentError, GentResult};
7use crate::interpreter::Value;
8use crate::Span;
9
10/// Call a method on a string value
11///
12/// # Arguments
13/// * `s` - The string to call the method on
14/// * `method` - The method name
15/// * `args` - Arguments to the method
16///
17/// # Supported Methods
18/// * `length()` - Returns the character count
19/// * `trim()` - Removes leading/trailing whitespace
20/// * `toLowerCase()` - Converts to lowercase
21/// * `toUpperCase()` - Converts to uppercase
22/// * `contains(substr)` - Checks if substring exists
23/// * `startsWith(prefix)` - Checks if string starts with prefix
24/// * `endsWith(suffix)` - Checks if string ends with suffix
25/// * `split(separator)` - Splits string by separator
26/// * `replace(old, new)` - Replaces first occurrence
27pub fn call_string_method(s: &str, method: &str, args: &[Value]) -> GentResult<Value> {
28    match method {
29        "length" => Ok(Value::Number(s.chars().count() as f64)),
30
31        "trim" => Ok(Value::String(s.trim().to_string())),
32
33        "toLowerCase" => Ok(Value::String(s.to_lowercase())),
34
35        "toUpperCase" => Ok(Value::String(s.to_uppercase())),
36
37        "contains" => {
38            let substr = get_string_arg(args, 0, "contains")?;
39            Ok(Value::Boolean(s.contains(&substr)))
40        }
41
42        "startsWith" => {
43            let prefix = get_string_arg(args, 0, "startsWith")?;
44            Ok(Value::Boolean(s.starts_with(&prefix)))
45        }
46
47        "endsWith" => {
48            let suffix = get_string_arg(args, 0, "endsWith")?;
49            Ok(Value::Boolean(s.ends_with(&suffix)))
50        }
51
52        "split" => {
53            let sep = get_string_arg(args, 0, "split")?;
54            let parts: Vec<Value> = s
55                .split(&sep)
56                .map(|p| Value::String(p.to_string()))
57                .collect();
58            Ok(Value::Array(parts))
59        }
60
61        "replace" => {
62            let old = get_string_arg(args, 0, "replace")?;
63            let new = get_string_arg(args, 1, "replace")?;
64            Ok(Value::String(s.replacen(&old, &new, 1)))
65        }
66
67        _ => Err(GentError::UndefinedProperty {
68            property: method.to_string(),
69            type_name: "String".to_string(),
70            span: Span::default(),
71        }),
72    }
73}
74
75/// Helper function to extract a string argument from the argument list
76fn get_string_arg(args: &[Value], index: usize, method: &str) -> GentResult<String> {
77    args.get(index)
78        .and_then(|v| match v {
79            Value::String(s) => Some(s.clone()),
80            _ => None,
81        })
82        .ok_or_else(|| {
83            let got = args
84                .get(index)
85                .map(|v| v.type_name())
86                .unwrap_or_else(|| "missing argument".to_string());
87            GentError::TypeError {
88                expected: format!("String argument for {}()", method),
89                got,
90                span: Span::default(),
91            }
92        })
93}