use crate::evaluator::RuntimeError;
use crate::value::Value;
pub fn split(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::WrongArity {
expected: 2,
got: args.len(),
});
}
match (&args[0], &args[1]) {
(Value::String(s), Value::String(sep)) => {
let parts: Vec<Value> = s
.split(sep.as_str())
.map(|p| Value::String(p.to_string()))
.collect();
Ok(Value::Array(parts))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "String, String".to_string(),
got: format!("{:?}, {:?}", args[0], args[1]),
}),
}
}
pub fn upper(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::String(s) => Ok(Value::String(s.to_uppercase())),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "String".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn lower(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::String(s) => Ok(Value::String(s.to_lowercase())),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "String".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn trim(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::String(s) => Ok(Value::String(s.trim().to_string())),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "String".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn contains(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::WrongArity {
expected: 2,
got: args.len(),
});
}
match (&args[0], &args[1]) {
(Value::String(s), Value::String(substr)) => {
Ok(Value::Boolean(s.contains(substr.as_str())))
}
(Value::Array(arr), item) => {
for elem in arr.iter() {
if values_equal(elem, item) {
return Ok(Value::Boolean(true));
}
}
Ok(Value::Boolean(false))
}
(Value::Dict(dict), Value::String(key)) => Ok(Value::Boolean(dict.contains_key(key))),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "(String, String) or (Array, Any) or (Dict, String)".to_string(),
got: format!("{:?}, {:?}", args[0], args[1]),
}),
}
}
fn values_equal(a: &Value, b: &Value) -> bool {
use Value::*;
match (a, b) {
(Number(a), Number(b)) => (a - b).abs() < f64::EPSILON,
(String(a), String(b)) => a == b,
(Boolean(a), Boolean(b)) => a == b,
(Null, Null) => true,
_ => false,
}
}
pub fn starts_with(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::WrongArity {
expected: 2,
got: args.len(),
});
}
match (&args[0], &args[1]) {
(Value::String(s), Value::String(prefix)) => {
Ok(Value::Boolean(s.starts_with(prefix.as_str())))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "String, String".to_string(),
got: format!("{:?}, {:?}", args[0], args[1]),
}),
}
}
pub fn ends_with(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::WrongArity {
expected: 2,
got: args.len(),
});
}
match (&args[0], &args[1]) {
(Value::String(s), Value::String(suffix)) => {
Ok(Value::Boolean(s.ends_with(suffix.as_str())))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "String, String".to_string(),
got: format!("{:?}, {:?}", args[0], args[1]),
}),
}
}
pub fn replace(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 3 {
return Err(RuntimeError::WrongArity {
expected: 3,
got: args.len(),
});
}
match (&args[0], &args[1], &args[2]) {
(Value::String(s), Value::String(from), Value::String(to)) => {
Ok(Value::String(s.replace(from.as_str(), to.as_str())))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "String, String, String".to_string(),
got: format!("{:?}, {:?}, {:?}", args[0], args[1], args[2]),
}),
}
}
pub fn repeat(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::WrongArity {
expected: 2,
got: args.len(),
});
}
match (&args[0], &args[1]) {
(Value::String(s), Value::Number(n)) => {
if *n < 0.0 || n.fract() != 0.0 {
return Err(RuntimeError::InvalidOperation(format!(
"Repeat count must be a non-negative integer, got {}",
n
)));
}
let count = *n as usize;
Ok(Value::String(s.repeat(count)))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "String, Number".to_string(),
got: format!("{:?}, {:?}", args[0], args[1]),
}),
}
}
pub fn substr(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 3 {
return Err(RuntimeError::WrongArity {
expected: 3,
got: args.len(),
});
}
match (&args[0], &args[1], &args[2]) {
(Value::String(s), Value::Number(start), Value::Number(end)) => {
if start.fract() != 0.0 || end.fract() != 0.0 {
return Err(RuntimeError::InvalidOperation(
"String indices must be integers".to_string(),
));
}
let start_idx = *start as i64;
let end_idx = *end as i64;
let len = s.len() as i64;
let start_idx = if start_idx < 0 {
(len + start_idx).max(0)
} else {
start_idx.min(len)
} as usize;
let end_idx = if end_idx < 0 {
(len + end_idx).max(0)
} else {
end_idx.min(len)
} as usize;
if start_idx > end_idx {
return Ok(Value::String(String::new()));
}
let result = s.get(start_idx..end_idx).unwrap_or("").to_string();
Ok(Value::String(result))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "String, Number, Number".to_string(),
got: format!("{:?}, {:?}, {:?}", args[0], args[1], args[2]),
}),
}
}
pub fn strlen(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::WrongArity {
expected: 1,
got: args.len(),
});
}
match &args[0] {
Value::String(s) => Ok(Value::Number(s.len() as f64)),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "String".to_string(),
got: format!("{:?}", args[0]),
}),
}
}
pub fn index_of(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::WrongArity {
expected: 2,
got: args.len(),
});
}
match (&args[0], &args[1]) {
(Value::String(s), Value::String(substr)) => match s.find(substr.as_str()) {
Some(pos) => Ok(Value::Number(pos as f64)),
None => Ok(Value::Number(-1.0)),
},
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "String, String".to_string(),
got: format!("{:?}, {:?}", args[0], args[1]),
}),
}
}
pub fn char_at(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::WrongArity {
expected: 2,
got: args.len(),
});
}
match (&args[0], &args[1]) {
(Value::String(s), Value::Number(idx)) => {
if idx.fract() != 0.0 {
return Err(RuntimeError::InvalidOperation(
"Index must be an integer".to_string(),
));
}
let index = *idx as i64;
let len = s.len() as i64;
let index = if index < 0 {
(len + index).max(0)
} else {
index.min(len)
} as usize;
let ch = s
.chars()
.nth(index)
.map(|c| c.to_string())
.unwrap_or_default();
Ok(Value::String(ch))
}
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "String, Number".to_string(),
got: format!("{:?}, {:?}", args[0], args[1]),
}),
}
}