use super::IntoFuncArgs; use crate::{ builtins::{iter::PySequenceIterator, PyDict, PyDictRef}, convert::ToPyObject, protocol::{PyIter, PyIterIter, PyMapping, PyMappingMethods}, AsObject, PyObject, PyObjectRef, PyResult, PyValue, TryFromObject, VirtualMachine, }; use std::{borrow::Borrow, marker::PhantomData}; #[derive(Clone, Debug)] pub struct ArgCallable { obj: PyObjectRef, } impl ArgCallable { #[inline(always)] pub fn invoke(&self, args: impl IntoFuncArgs, vm: &VirtualMachine) -> PyResult { vm.invoke(&self.obj, args) } } impl Borrow for ArgCallable { #[inline(always)] fn borrow(&self) -> &PyObject { &self.obj } } impl AsRef for ArgCallable { #[inline(always)] fn as_ref(&self) -> &PyObject { &self.obj } } impl From for PyObjectRef { #[inline(always)] fn from(value: ArgCallable) -> PyObjectRef { value.obj } } impl TryFromObject for ArgCallable { fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { if vm.is_callable(&obj) { Ok(ArgCallable { obj }) } else { Err(vm.new_type_error(format!("'{}' object is not callable", obj.class().name()))) } } } /// An iterable Python object. /// /// `ArgIterable` implements `FromArgs` so that a built-in function can accept /// an object that is required to conform to the Python iterator protocol. /// /// ArgIterable can optionally perform type checking and conversions on iterated /// objects using a generic type parameter that implements `TryFromObject`. pub struct ArgIterable { iterable: PyObjectRef, iterfn: Option, _item: PhantomData, } impl ArgIterable { /// Returns an iterator over this sequence of objects. /// /// This operation may fail if an exception is raised while invoking the /// `__iter__` method of the iterable object. pub fn iter<'a>(&self, vm: &'a VirtualMachine) -> PyResult> { 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 TryFromObject for ArgIterable where T: TryFromObject, { fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { let iterfn; { let cls = obj.class(); iterfn = cls.mro_find_map(|x| x.slots.iter.load()); if iterfn.is_none() && !cls.has_attr("__getitem__") { return Err(vm.new_type_error(format!("'{}' object is not iterable", cls.name()))); } } Ok(Self { iterable: obj, iterfn, _item: PhantomData, }) } } #[derive(Clone)] pub struct ArgMapping { obj: PyObjectRef, mapping_methods: PyMappingMethods, } impl ArgMapping { #[inline(always)] pub fn from_dict_exact(dict: PyDictRef) -> Self { Self { obj: dict.into(), mapping_methods: PyDict::MAPPING_METHODS, } } #[inline(always)] pub fn mapping(&self) -> PyMapping { PyMapping::with_methods(&self.obj, self.mapping_methods) } } impl Borrow for ArgMapping { #[inline(always)] fn borrow(&self) -> &PyObject { &self.obj } } impl AsRef for ArgMapping { #[inline(always)] fn as_ref(&self) -> &PyObject { &self.obj } } impl From 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 { let mapping = PyMapping::try_protocol(&obj, vm)?; let mapping_methods = *mapping.methods(vm); Ok(Self { obj, mapping_methods, }) } } // this is not strictly related to PySequence protocol. #[derive(Clone)] pub struct ArgSequence(Vec); impl ArgSequence { #[inline(always)] pub fn into_vec(self) -> Vec { self.0 } #[inline(always)] pub fn as_slice(&self) -> &[T] { &self.0 } } impl TryFromObject for ArgSequence { fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { obj.try_to_value(vm).map(Self) } }