use crate::core::{Args, Value};
use anyhow::{anyhow, Result};
pub fn string_len(self_value: &Value, mut args: Args) -> Result<Value> {
args.set_function_name("len");
args.complete()?;
if let Value::String(s) = self_value {
Ok(Value::Int(s.chars().count() as i64))
} else {
Err(anyhow!("len() called on non-string value"))
}
}
pub fn string_to_list(self_value: &Value, mut args: Args) -> Result<Value> {
args.set_function_name("to_list");
args.complete()?;
if let Value::String(s) = self_value {
let chars: Vec<Value> = s.chars().map(|c| Value::String(c.to_string())).collect();
Ok(Value::new_list_with_values(chars))
} else {
Err(anyhow!("to_list() called on non-string value"))
}
}
pub fn list_len(self_value: &Value, mut args: Args) -> Result<Value> {
args.set_function_name("len");
args.complete()?;
if let Value::List(list_ref) = self_value {
let list = list_ref.borrow();
Ok(Value::Int(list.len() as i64))
} else {
Err(anyhow!("len() called on non-list value"))
}
}
pub fn list_append(self_value: &Value, mut args: Args) -> Result<Value> {
args.set_function_name("append");
let item = args.expect("item")?;
args.complete()?;
if let Value::List(list_ref) = self_value {
list_ref.borrow_mut().push(item);
Ok(self_value.clone()) } else {
Err(anyhow!("append() called on non-list value"))
}
}
pub fn list_filter(self_value: &Value, mut args: Args) -> Result<Value> {
args.set_function_name("filter");
let predicate = args.expect("predicate")?;
args.complete()?;
if let Value::List(list_ref) = self_value {
let list = list_ref.borrow();
let mut filtered = Vec::new();
for item in list.iter() {
let predicate_args = crate::core::Args::positional(vec![item.clone()]);
let result = predicate.call(predicate_args)?;
if result.is_truthy() {
filtered.push(item.clone());
}
}
Ok(Value::new_list_with_values(filtered))
} else {
Err(anyhow!("filter() called on non-list value"))
}
}
pub fn list_map(self_value: &Value, mut args: Args) -> Result<Value> {
args.set_function_name("map");
let mapper = args.expect("mapper")?;
args.complete()?;
if let Value::List(list_ref) = self_value {
let list = list_ref.borrow();
let mut mapped = Vec::new();
for item in list.iter() {
let mapper_args = crate::core::Args::positional(vec![item.clone()]);
let result = mapper.call(mapper_args)?;
mapped.push(result);
}
Ok(Value::new_list_with_values(mapped))
} else {
Err(anyhow!("map() called on non-list value"))
}
}
pub fn list_reduce(self_value: &Value, mut args: Args) -> Result<Value> {
args.set_function_name("reduce");
let reducer = args.expect("reducer")?;
let initial = args.optional("initial", Value::Null);
args.complete()?;
if let Value::List(list_ref) = self_value {
let list = list_ref.borrow();
if list.is_empty() {
if matches!(initial, Value::Null) {
return Err(anyhow!("reduce() of empty sequence with no initial value"));
}
return Ok(initial);
}
let is_initial_null = matches!(initial, Value::Null);
let mut accumulator = if is_initial_null {
list[0].clone()
} else {
initial
};
let start_index = if is_initial_null { 1 } else { 0 };
for item in list.iter().skip(start_index) {
let reducer_args = crate::core::Args::positional(vec![accumulator, item.clone()]);
accumulator = reducer.call(reducer_args)?;
}
Ok(accumulator)
} else {
Err(anyhow!("reduce() called on non-list value"))
}
}
pub fn list_sum(self_value: &Value, mut args: Args) -> Result<Value> {
args.set_function_name("sum");
args.complete()?;
if let Value::List(list_ref) = self_value {
let list = list_ref.borrow();
if list.is_empty() {
return Ok(Value::Int(0));
}
let mut sum = list[0].clone();
for item in list.iter().skip(1) {
sum = crate::core::builtins::op_add(
&sum,
crate::core::Args::positional(vec![item.clone()]),
)?;
}
Ok(sum)
} else {
Err(anyhow!("sum() called on non-list value"))
}
}