use super::super::{MethodOutcome, arg1, to_index, to_len_i64, value_to_i64};
use crate::{
error::{EvalError, InterpreterError},
eval::control_flow::iterate_value,
state::estimate_value_size,
value::{ExceptionValue, Value, shared_list},
};
pub(crate) fn dispatch_list_method(
items: &mut Vec<Value>,
method: &str,
args: &[Value],
) -> Result<MethodOutcome, EvalError> {
use crate::eval::operations::values_equal_pub;
match method {
"copy" => Ok(MethodOutcome::pure(Value::List(shared_list(items.clone())))),
"count" => {
let target = arg1(method, args)?;
let count = items.iter().filter(|v| values_equal_pub(v, target)).count();
Ok(MethodOutcome::pure(Value::Int(to_len_i64(count)?)))
}
"index" => {
let target = arg1(method, args)?;
for (i, item) in items.iter().enumerate() {
if values_equal_pub(item, target) {
return Ok(MethodOutcome::pure(Value::Int(to_len_i64(i)?)));
}
}
Err(EvalError::Exception(ExceptionValue::new(
"ValueError",
format!("{} is not in list", target.repr()),
)))
}
"append" => {
let arg = arg1(method, args)?;
let size = estimate_value_size(arg);
items.push(arg.clone());
Ok(MethodOutcome::grew(Value::None, size))
}
"extend" => {
let new_items = iterate_value(arg1(method, args)?)?;
let added: usize = new_items.iter().map(estimate_value_size).sum();
items.extend(new_items);
Ok(MethodOutcome::grew(Value::None, added))
}
"insert" => {
if args.len() != 2 {
return Err(InterpreterError::TypeError(
"insert() takes exactly 2 arguments".into(),
)
.into());
}
let idx = value_to_i64(&args[0])?;
let size = estimate_value_size(&args[1]);
let len = to_len_i64(items.len())?;
let pos = if idx < 0 {
to_index((len + idx).max(0))?
} else {
to_index(idx)?.min(items.len())
};
items.insert(pos, args[1].clone());
Ok(MethodOutcome::grew(Value::None, size))
}
"pop" => {
if items.is_empty() {
return Err(EvalError::Exception(ExceptionValue::new(
"IndexError",
"pop from empty list",
)));
}
let i = match args.first() {
None => items.len() - 1,
Some(arg) => {
let raw = value_to_i64(arg)?;
let len = to_len_i64(items.len())?;
let normalized = if raw < 0 { len + raw } else { raw };
if normalized < 0 || normalized >= len {
return Err(EvalError::Exception(ExceptionValue::index_error("pop")));
}
to_index(normalized)?
}
};
let val = items.remove(i);
let freed = estimate_value_size(&val);
Ok(MethodOutcome::shrank(val, freed))
}
"remove" => {
let target = arg1(method, args)?;
let Some(idx) = items.iter().position(|v| values_equal_pub(v, target)) else {
return Err(EvalError::Exception(ExceptionValue::new(
"ValueError",
"list.remove(x): x not in list",
)));
};
let removed = items.remove(idx);
Ok(MethodOutcome::shrank(Value::None, estimate_value_size(&removed)))
}
"reverse" => {
items.reverse();
Ok(MethodOutcome::pure(Value::None))
}
"clear" => {
let freed: usize = items.iter().map(estimate_value_size).sum();
items.clear();
Ok(MethodOutcome::shrank(Value::None, freed))
}
_ => Err(InterpreterError::AttributeError(format!(
"'list' object has no attribute '{method}'"
))
.into()),
}
}