use std::collections::VecDeque;
use super::super::{MethodOutcome, arg1};
use crate::{
error::{EvalError, InterpreterError},
eval::{control_flow::iterate_value, place},
state::estimate_value_size,
value::{ExceptionValue, Value},
};
pub(crate) fn dispatch_deque_method(
items: &mut VecDeque<Value>,
maxlen: Option<&usize>,
method: &str,
args: &[Value],
kwargs: &indexmap::IndexMap<String, Value>,
) -> Result<MethodOutcome, EvalError> {
crate::eval::functions::reject_kwargs(method, kwargs)?;
match method {
"append" => {
let val = arg1(method, args)?.clone();
let added = place::to_isize(estimate_value_size(&val));
items.push_back(val);
let mut delta = added;
if let Some(cap) = maxlen {
while items.len() > *cap {
if let Some(dropped) = items.pop_front() {
delta =
delta.saturating_sub(place::to_isize(estimate_value_size(&dropped)));
}
}
}
Ok(MethodOutcome { value: Value::None, mem_delta: delta })
}
"appendleft" => {
let val = arg1(method, args)?.clone();
let added = place::to_isize(estimate_value_size(&val));
items.push_front(val);
let mut delta = added;
if let Some(cap) = maxlen {
while items.len() > *cap {
if let Some(dropped) = items.pop_back() {
delta =
delta.saturating_sub(place::to_isize(estimate_value_size(&dropped)));
}
}
}
Ok(MethodOutcome { value: Value::None, mem_delta: delta })
}
"pop" => {
let popped = items.pop_back().ok_or_else(|| {
EvalError::Exception(ExceptionValue::new("IndexError", "pop from an empty deque"))
})?;
let freed = place::to_isize(estimate_value_size(&popped));
Ok(MethodOutcome { value: popped, mem_delta: -freed })
}
"popleft" => {
let popped = items.pop_front().ok_or_else(|| {
EvalError::Exception(ExceptionValue::new("IndexError", "pop from an empty deque"))
})?;
let freed = place::to_isize(estimate_value_size(&popped));
Ok(MethodOutcome { value: popped, mem_delta: -freed })
}
"extend" => {
let iter = iterate_value(arg1(method, args)?)?;
let mut delta = 0isize;
for val in iter {
delta = delta.saturating_add(place::to_isize(estimate_value_size(&val)));
items.push_back(val);
if let Some(cap) = maxlen {
while items.len() > *cap {
if let Some(dropped) = items.pop_front() {
delta = delta
.saturating_sub(place::to_isize(estimate_value_size(&dropped)));
}
}
}
}
Ok(MethodOutcome { value: Value::None, mem_delta: delta })
}
"extendleft" => {
let iter = iterate_value(arg1(method, args)?)?;
let mut delta = 0isize;
for val in iter {
delta = delta.saturating_add(place::to_isize(estimate_value_size(&val)));
items.push_front(val);
if let Some(cap) = maxlen {
while items.len() > *cap {
if let Some(dropped) = items.pop_back() {
delta = delta
.saturating_sub(place::to_isize(estimate_value_size(&dropped)));
}
}
}
}
Ok(MethodOutcome { value: Value::None, mem_delta: delta })
}
"rotate" => {
let n = match args.first() {
None => 1i64,
Some(Value::Int(n)) => *n,
Some(Value::Bool(b)) => i64::from(*b),
Some(other) => {
return Err(InterpreterError::TypeError(format!(
"rotate() expected an integer (got '{}')",
other.type_name()
))
.into());
}
};
if items.is_empty() {
return Ok(MethodOutcome::pure(Value::None));
}
let len = i64::try_from(items.len()).map_err(|_| {
EvalError::from(InterpreterError::Runtime("deque length overflows i64".into()))
})?;
let shift_signed = n.rem_euclid(len);
let shift = usize::try_from(shift_signed).map_err(|_| {
EvalError::from(InterpreterError::Runtime("rotate shift out of range".into()))
})?;
items.rotate_right(shift);
Ok(MethodOutcome::pure(Value::None))
}
"clear" => {
let freed: usize = items.iter().map(estimate_value_size).sum();
items.clear();
Ok(MethodOutcome::shrank(Value::None, freed))
}
"copy" => {
let copied = Value::Deque { items: items.clone(), maxlen: maxlen.copied() };
Ok(MethodOutcome::pure(copied))
}
"index" => {
let target = arg1(method, args)?;
for (i, item) in items.iter().enumerate() {
if crate::eval::operations::values_equal_pub(item, target) {
return Ok(MethodOutcome::pure(Value::Int(i as i64)));
}
}
Err(InterpreterError::ValueError(format!("{} is not in deque", target.repr())).into())
}
"count" => {
let target = arg1(method, args)?;
let n = items
.iter()
.filter(|it| crate::eval::operations::values_equal_pub(it, target))
.count();
Ok(MethodOutcome::pure(Value::Int(n as i64)))
}
"insert" => {
let raw = match args.first() {
Some(Value::Int(i)) => *i,
Some(Value::Bool(b)) => i64::from(*b),
_ => {
return Err(InterpreterError::TypeError(
"insert() argument 1 must be an integer".into(),
)
.into());
}
};
let value = args.get(1).cloned().unwrap_or(Value::None);
if maxlen.is_some_and(|&m| items.len() >= m) {
return Err(EvalError::Exception(ExceptionValue::new(
"IndexError",
"deque already at its maximum size",
)));
}
let len = items.len() as i64;
let idx = raw.clamp(-len, len);
let idx = if idx < 0 { (idx + len).max(0) } else { idx.min(len) } as usize;
let grew = estimate_value_size(&value);
items.insert(idx, value);
Ok(MethodOutcome::grew(Value::None, grew))
}
"remove" => {
let target = arg1(method, args)?;
let pos =
items.iter().position(|it| crate::eval::operations::values_equal_pub(it, target));
match pos {
Some(i) => {
let removed = items.remove(i);
let freed = removed.as_ref().map_or(0, estimate_value_size);
Ok(MethodOutcome::shrank(Value::None, freed))
}
None => {
Err(InterpreterError::ValueError("deque.remove(x): x not in deque".into())
.into())
}
}
}
"reverse" => {
items.make_contiguous().reverse();
Ok(MethodOutcome::pure(Value::None))
}
_ => Err(InterpreterError::AttributeError(format!(
"'deque' object has no attribute '{method}'"
))
.into()),
}
}