use indexmap::IndexMap;
use super::{methods, resolve_proxy};
use crate::{
error::{EvalError, InterpreterError},
eval::place,
value::{Value, shared_list},
};
#[derive(Clone, Copy)]
pub(crate) struct CallArgs<'a> {
pub positional: &'a [Value],
pub keyword: &'a IndexMap<String, Value>,
}
pub(crate) struct MethodOutcome {
pub value: Value,
pub mem_delta: isize,
}
impl MethodOutcome {
pub(crate) const fn pure(value: Value) -> Self {
Self { value, mem_delta: 0 }
}
pub(crate) fn grew(value: Value, bytes: usize) -> Self {
Self { value, mem_delta: place::to_isize(bytes) }
}
pub(crate) fn shrank(value: Value, bytes: usize) -> Self {
Self { value, mem_delta: -place::to_isize(bytes) }
}
}
pub(super) async fn resolve_method_args(args: &[Value]) -> Result<Vec<Value>, EvalError> {
let mut resolved_args = Vec::with_capacity(args.len());
for arg in args {
let resolved = resolve_proxy(arg).await?;
match resolved {
Value::List(items) => {
let snapshot = items.lock().clone();
let mut resolved_items = Vec::with_capacity(snapshot.len());
for item in &snapshot {
resolved_items.push(resolve_proxy(item).await?);
}
resolved_args.push(Value::List(shared_list(resolved_items)));
}
Value::Tuple(items) => {
let mut resolved_items = Vec::with_capacity(items.len());
for item in &items {
resolved_items.push(resolve_proxy(item).await?);
}
resolved_args.push(Value::Tuple(resolved_items));
}
other => resolved_args.push(other),
}
}
Ok(resolved_args)
}
pub(super) fn dispatch_method(
obj: &mut Value,
method: &str,
args: &[Value],
) -> Result<MethodOutcome, EvalError> {
match obj {
Value::String(s) => {
methods::str::dispatch_string_method(s, method, args).map(MethodOutcome::pure)
}
Value::List(items) => {
let mut guard = items.lock();
methods::list::dispatch_list_method(&mut guard, method, args)
}
Value::Dict(map) => methods::dict::dispatch_dict_method(map, method, args),
Value::Counter(map) => methods::counter::dispatch_counter_method(map, method, args),
Value::Deque { items, maxlen } => {
methods::deque::dispatch_deque_method(items, maxlen.as_ref(), method, args)
}
Value::DefaultDict(data) => {
methods::dict::dispatch_dict_method(&mut data.items, method, args)
}
Value::Set(items) => methods::set::dispatch_set_method(items, method, args),
Value::Tuple(items) => {
methods::tuple::dispatch_tuple_method(items, method, args).map(MethodOutcome::pure)
}
Value::Date(date) => {
crate::eval::modules::datetime::dispatch_date_method(*date, method, args)
.map(MethodOutcome::pure)
}
Value::DateTime { dt, tz_offset_secs } => {
crate::eval::modules::datetime::dispatch_datetime_method(
*dt,
*tz_offset_secs,
method,
args,
)
.map(MethodOutcome::pure)
}
Value::Time(t) => crate::eval::modules::datetime::dispatch_time_method(*t, method, args)
.map(MethodOutcome::pure),
Value::TimeDelta(micros) => {
crate::eval::modules::datetime::dispatch_timedelta_method(*micros, method, args)
.map(MethodOutcome::pure)
}
Value::ReMatch(m) => crate::eval::modules::re::dispatch_match_method(m, method, args)
.map(MethodOutcome::pure),
Value::HashDigest { algo, bytes } => {
crate::eval::modules::hashlib::dispatch_hash_method(algo, bytes, method, args)
.map(MethodOutcome::pure)
}
Value::Int(i) => {
methods::int::dispatch_int_method(*i, method, args).map(MethodOutcome::pure)
}
Value::Bytes(b) => {
methods::bytes::dispatch_bytes_method(b, method, args).map(MethodOutcome::pure)
}
_ => Err(InterpreterError::AttributeError(format!(
"'{}' object has no attribute '{method}'",
obj.type_name()
))
.into()),
}
}
pub(crate) fn arg1<'a>(method: &str, args: &'a [Value]) -> Result<&'a Value, EvalError> {
args.first().ok_or_else(|| {
EvalError::from(InterpreterError::TypeError(format!("{method}() takes exactly 1 argument")))
})
}