use super::IntoFuncArgs;
use crate::{
builtins::{iter::PySequenceIterator, PyDict, PyDictRef},
convert::ToPyObject,
identifier,
object::{Traverse, TraverseFn},
protocol::{PyIter, PyIterIter, PyMapping, PyMappingMethods},
types::{AsMapping, GenericMethod},
AsObject, PyObject, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine,
};
use std::{borrow::Borrow, marker::PhantomData, ops::Deref};
#[derive(Clone, Traverse)]
pub struct ArgCallable {
obj: PyObjectRef,
#[pytraverse(skip)]
call: GenericMethod,
}
impl ArgCallable {
#[inline(always)]
pub fn invoke(&self, args: impl IntoFuncArgs, vm: &VirtualMachine) -> PyResult {
let args = args.into_args(vm);
(self.call)(&self.obj, args, vm)
}
}
impl std::fmt::Debug for ArgCallable {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ArgCallable")
.field("obj", &self.obj)
.field("call", &format!("{:08x}", self.call as usize))
.finish()
}
}
impl Borrow<PyObject> for ArgCallable {
#[inline(always)]
fn borrow(&self) -> &PyObject {
&self.obj
}
}
impl AsRef<PyObject> for ArgCallable {
#[inline(always)]
fn as_ref(&self) -> &PyObject {
&self.obj
}
}
impl From<ArgCallable> for PyObjectRef {
#[inline(always)]
fn from(value: ArgCallable) -> PyObjectRef {
value.obj
}
}
impl TryFromObject for ArgCallable {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
let Some(callable) = obj.to_callable() else {
return Err(
vm.new_type_error(format!("'{}' object is not callable", obj.class().name()))
);
};
let call = callable.call;
Ok(ArgCallable { obj, call })
}
}
pub struct ArgIterable<T = PyObjectRef> {
iterable: PyObjectRef,
iterfn: Option<crate::types::IterFunc>,
_item: PhantomData<T>,
}
unsafe impl<T: Traverse> Traverse for ArgIterable<T> {
fn traverse(&self, tracer_fn: &mut TraverseFn) {
self.iterable.traverse(tracer_fn)
}
}
impl<T> ArgIterable<T> {
pub fn iter<'a>(&self, vm: &'a VirtualMachine) -> PyResult<PyIterIter<'a, T>> {
let iter = PyIter::new(match self.iterfn {
Some(f) => f(self.iterable.clone(), vm)?,
None => PySequenceIterator::new(self.iterable.clone(), vm)?.into_pyobject(vm),
});
iter.into_iter(vm)
}
}
impl<T> TryFromObject for ArgIterable<T>
where
T: TryFromObject,
{
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
let iterfn = {
let cls = obj.class();
let iterfn = cls.mro_find_map(|x| x.slots.iter.load());
if iterfn.is_none() && !cls.has_attr(identifier!(vm, __getitem__)) {
return Err(vm.new_type_error(format!("'{}' object is not iterable", cls.name())));
}
iterfn
};
Ok(Self {
iterable: obj,
iterfn,
_item: PhantomData,
})
}
}
#[derive(Debug, Clone, Traverse)]
pub struct ArgMapping {
obj: PyObjectRef,
#[pytraverse(skip)]
methods: &'static PyMappingMethods,
}
impl ArgMapping {
#[inline]
pub fn with_methods(obj: PyObjectRef, methods: &'static PyMappingMethods) -> Self {
Self { obj, methods }
}
#[inline(always)]
pub fn from_dict_exact(dict: PyDictRef) -> Self {
Self {
obj: dict.into(),
methods: PyDict::as_mapping(),
}
}
#[inline(always)]
pub fn mapping(&self) -> PyMapping {
PyMapping {
obj: &self.obj,
methods: self.methods,
}
}
}
impl Borrow<PyObject> for ArgMapping {
#[inline(always)]
fn borrow(&self) -> &PyObject {
&self.obj
}
}
impl AsRef<PyObject> for ArgMapping {
#[inline(always)]
fn as_ref(&self) -> &PyObject {
&self.obj
}
}
impl Deref for ArgMapping {
type Target = PyObject;
#[inline(always)]
fn deref(&self) -> &PyObject {
&self.obj
}
}
impl From<ArgMapping> for PyObjectRef {
#[inline(always)]
fn from(value: ArgMapping) -> PyObjectRef {
value.obj
}
}
impl ToPyObject for ArgMapping {
#[inline(always)]
fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
self.obj
}
}
impl TryFromObject for ArgMapping {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
let mapping = PyMapping::try_protocol(&obj, vm)?;
let methods = mapping.methods;
Ok(Self { obj, methods })
}
}
#[derive(Clone)]
pub struct ArgSequence<T = PyObjectRef>(Vec<T>);
unsafe impl<T: Traverse> Traverse for ArgSequence<T> {
fn traverse(&self, tracer_fn: &mut TraverseFn) {
self.0.traverse(tracer_fn);
}
}
impl<T> ArgSequence<T> {
#[inline(always)]
pub fn into_vec(self) -> Vec<T> {
self.0
}
#[inline(always)]
pub fn as_slice(&self) -> &[T] {
&self.0
}
}
impl<T> std::ops::Deref for ArgSequence<T> {
type Target = [T];
#[inline(always)]
fn deref(&self) -> &[T] {
self.as_slice()
}
}
impl<T: TryFromObject> TryFromObject for ArgSequence<T> {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
obj.try_to_value(vm).map(Self)
}
}