use std::{cmp::Ordering, fmt::Write};
use ahash::AHashSet;
use super::{Type, allocate_string};
use crate::{
args::ArgValues,
bytecode::{CallResult, VM},
exception_private::{ExcType, RunResult, SimpleException},
hash::HashValue,
heap::{DropWithHeap, HeapId},
intern::StringId,
os::OsFunctionCall,
resource::{ResourceError, ResourceTracker},
value::{EitherStr, Value},
};
#[derive(Debug)]
pub enum AttrCallResult {
Value(Value),
OsCall(OsFunctionCall),
#[expect(dead_code)]
ExternalCall(StringId, ArgValues),
}
impl From<AttrCallResult> for CallResult {
fn from(result: AttrCallResult) -> Self {
match result {
AttrCallResult::Value(v) => Self::Value(v),
AttrCallResult::OsCall(call) => Self::OsCall(call),
AttrCallResult::ExternalCall(ext_id, args) => Self::External(EitherStr::Interned(ext_id), args),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum CmpOrder {
Ordered(Ordering),
Unordered,
Incomparable,
}
impl CmpOrder {
pub(crate) fn from_numeric(ordering: Option<Ordering>) -> Self {
match ordering {
Some(ordering) => Self::Ordered(ordering),
None => Self::Unordered,
}
}
pub(crate) fn from_total(ordering: Option<Ordering>) -> Self {
match ordering {
Some(ordering) => Self::Ordered(ordering),
None => Self::Incomparable,
}
}
}
pub(crate) trait PyTrait<'h> {
fn py_type(&self, vm: &VM<'h, impl ResourceTracker>) -> Type;
fn py_len(&self, vm: &VM<'h, impl ResourceTracker>) -> Option<usize>;
fn py_hash(&self, _self_id: HeapId, _vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Option<HashValue>> {
Ok(None)
}
fn py_eq_impl(&self, other: &Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Option<bool>>;
fn py_cmp(&self, _other: &Self, _vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<CmpOrder> {
Ok(CmpOrder::Incomparable)
}
fn py_bool(&self, vm: &mut VM<'h, impl ResourceTracker>) -> bool {
self.py_len(vm) != Some(0)
}
fn py_repr_fmt(
&self,
f: &mut impl Write,
vm: &mut VM<'h, impl ResourceTracker>,
heap_ids: &mut LazyHeapSet,
) -> RunResult<()>;
fn py_repr(&self, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Value> {
let mut s = String::new();
let mut heap_ids = LazyHeapSet::default();
self.py_repr_fmt(&mut s, vm, &mut heap_ids)?;
Ok(allocate_string(s, vm.heap)?)
}
fn py_str(&self, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Value> {
self.py_repr(vm)
}
fn py_add(&self, _other: &Self, _vm: &mut VM<'h, impl ResourceTracker>) -> Result<Option<Value>, ResourceError> {
Ok(None)
}
fn py_sub(&self, _other: &Self, _vm: &mut VM<'h, impl ResourceTracker>) -> Result<Option<Value>, ResourceError> {
Ok(None)
}
fn py_mod(&self, _other: &Self, _vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Option<Value>> {
Ok(None)
}
fn py_mod_eq(&self, _other: &Self, _right_value: i64) -> Option<bool> {
None
}
fn py_iadd(
&mut self,
_other: &Value,
_vm: &mut VM<'h, impl ResourceTracker>,
_self_id: Option<HeapId>,
) -> Result<bool, ResourceError> {
Ok(false)
}
fn py_mult(&self, _other: &Self, _vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Option<Value>> {
Ok(None)
}
fn py_div(&self, _other: &Self, _vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Option<Value>> {
Ok(None)
}
fn py_floordiv(&self, _other: &Self, _vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Option<Value>> {
Ok(None)
}
fn py_pow(&self, _other: &Self, _vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Option<Value>> {
Ok(None)
}
fn py_call_attr(
&mut self,
_self_id: HeapId,
vm: &mut VM<'h, impl ResourceTracker>,
attr: &EitherStr,
args: ArgValues,
) -> RunResult<CallResult> {
args.drop_with_heap(vm);
Err(ExcType::attribute_error(
self.py_type(vm).name(vm.heap, vm.interns),
attr.as_str(vm.interns),
))
}
fn py_is_context_manager(&self, _vm: &VM<'h, impl ResourceTracker>) -> bool {
false
}
fn py_enter(&mut self, _self_id: HeapId, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<CallResult> {
Err(ExcType::attribute_error(
self.py_type(vm).name(vm.heap, vm.interns),
"__enter__",
))
}
fn py_exit(
&mut self,
_self_id: HeapId,
vm: &mut VM<'h, impl ResourceTracker>,
_exc: Option<HeapId>,
) -> RunResult<CallResult> {
Err(ExcType::attribute_error(
self.py_type(vm).name(vm.heap, vm.interns),
"__exit__",
))
}
fn py_getitem(&self, _key: &Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Value> {
Err(ExcType::type_error_not_sub(&self.py_type(vm).name(vm.heap, vm.interns)))
}
fn py_setitem(&mut self, key: Value, value: Value, vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<()> {
key.drop_with_heap(vm);
value.drop_with_heap(vm);
Err(SimpleException::new_msg(
ExcType::TypeError,
format!(
"'{}' object does not support item assignment",
self.py_type(vm).name(vm.heap, vm.interns)
),
)
.into())
}
fn py_getattr(&self, _attr: &EitherStr, _vm: &mut VM<'h, impl ResourceTracker>) -> RunResult<Option<CallResult>> {
Ok(None)
}
}
#[derive(Default, Debug, Clone)]
pub(crate) struct LazyHeapSet(Option<AHashSet<HeapId>>);
impl LazyHeapSet {
pub fn insert(&mut self, heap_id: HeapId) {
if let Some(s) = self.0.as_mut() {
s.insert(heap_id);
} else {
let mut s = AHashSet::default();
s.insert(heap_id);
self.0 = Some(s);
}
}
#[expect(clippy::trivially_copy_pass_by_ref, reason = "Match AHashSet method")]
pub fn contains(&self, heap_id: &HeapId) -> bool {
self.0.as_ref().is_some_and(|s| s.contains(heap_id))
}
#[expect(clippy::trivially_copy_pass_by_ref, reason = "Match AHashSet method")]
pub fn remove(&mut self, heap_id: &HeapId) {
if let Some(s) = self.0.as_mut() {
s.remove(heap_id);
}
}
}