use std::borrow::Cow;
use crate::{
bytecode::VM,
exception_private::{ExcType, RunError, RunResult, SimpleException},
heap::{ContainsHeap, DropWithHeap, HeapData},
resource::ResourceTracker,
types::PyTrait,
value::Value,
};
pub(crate) enum ArgErrCtx {
Plain,
BadArgPos { func_name: &'static str, pos: usize },
BadArgNamed {
func_name: &'static str,
arg_name: &'static str,
},
}
pub(crate) enum FromValueFail {
WrongType,
Raise(RunError),
}
pub(crate) trait FromValue: Sized {
const EXPECTED_TYPE_NAME: Option<&'static str> = None;
fn from_value(value: Value, vm: &mut VM<'_, impl ResourceTracker>) -> Result<Self, FromValueFail>;
fn type_error(got: &str) -> RunError {
let expected = Self::EXPECTED_TYPE_NAME.unwrap_or("a different type");
ExcType::type_error(format!("expected {expected}, not {got}"))
}
fn drop_extracted(self, heap: &mut impl ContainsHeap) {
let _ = heap;
drop(self);
}
fn extract_into(
value: Value,
slot: &mut Option<Self>,
vm: &mut VM<'_, impl ResourceTracker>,
ctx: ArgErrCtx,
) -> RunResult<()> {
let got_name =
Self::EXPECTED_TYPE_NAME.map(|_| value.py_type_heap(vm.heap).cpython_arg_name(vm.heap, vm.interns));
match Self::from_value(value, vm) {
Ok(extracted) => {
*slot = Some(extracted);
Ok(())
}
Err(FromValueFail::Raise(err)) => Err(err),
Err(FromValueFail::WrongType) => {
let got = got_name.unwrap_or(Cow::Borrowed("object"));
Err(match (ctx, Self::EXPECTED_TYPE_NAME) {
(ArgErrCtx::BadArgPos { func_name, pos }, Some(expected)) => {
ExcType::type_error_bad_arg_pos(func_name, pos, expected, got)
}
(ArgErrCtx::BadArgNamed { func_name, arg_name }, Some(expected)) => {
ExcType::type_error_bad_arg_named(func_name, arg_name, expected, got)
}
_ => Self::type_error(&got),
})
}
}
}
}
impl FromValue for Value {
fn from_value(value: Self, _vm: &mut VM<'_, impl ResourceTracker>) -> Result<Self, FromValueFail> {
Ok(value)
}
fn drop_extracted(self, heap: &mut impl ContainsHeap) {
self.drop_with_heap(heap);
}
}
impl FromValue for i32 {
const EXPECTED_TYPE_NAME: Option<&'static str> = Some("int");
fn from_value(value: Value, vm: &mut VM<'_, impl ResourceTracker>) -> Result<Self, FromValueFail> {
let result = match value {
Value::Bool(b) => Ok(Self::from(b)),
Value::Int(i) => Self::try_from(i).map_err(|_| {
let msg = if i < 0 {
"signed integer is less than minimum"
} else {
"signed integer is greater than maximum"
};
FromValueFail::Raise(SimpleException::new_msg(ExcType::OverflowError, msg).into())
}),
_ if is_long_int(&value, vm) => Err(FromValueFail::Raise(ExcType::overflow_c_long())),
_ => Err(FromValueFail::WrongType),
};
value.drop_with_heap(vm);
result
}
fn type_error(got: &str) -> RunError {
ExcType::type_error_not_integer(got)
}
}
impl FromValue for i64 {
const EXPECTED_TYPE_NAME: Option<&'static str> = Some("int");
fn from_value(value: Value, vm: &mut VM<'_, impl ResourceTracker>) -> Result<Self, FromValueFail> {
let result = match value {
Value::Bool(b) => Ok(Self::from(b)),
Value::Int(i) => Ok(i),
_ if is_long_int(&value, vm) => Err(FromValueFail::Raise(ExcType::overflow_c_long())),
_ => Err(FromValueFail::WrongType),
};
value.drop_with_heap(vm);
result
}
fn type_error(got: &str) -> RunError {
ExcType::type_error_not_integer(got)
}
}
pub(crate) fn is_long_int(value: &Value, vm: &VM<'_, impl ResourceTracker>) -> bool {
match value {
Value::InternLongInt(_) => true,
Value::Ref(id) => matches!(vm.heap.get(*id), HeapData::LongInt(_)),
_ => false,
}
}
impl FromValue for bool {
const EXPECTED_TYPE_NAME: Option<&'static str> = Some("bool");
fn from_value(value: Value, vm: &mut VM<'_, impl ResourceTracker>) -> Result<Self, FromValueFail> {
let result = match value {
Value::Bool(b) => Ok(b),
_ => Err(FromValueFail::WrongType),
};
value.drop_with_heap(vm);
result
}
fn type_error(_got: &str) -> RunError {
SimpleException::new_msg(ExcType::TypeError, "a bool is required").into()
}
}
pub(crate) struct StrArg(Value);
impl FromValue for StrArg {
const EXPECTED_TYPE_NAME: Option<&'static str> = Some("str");
fn from_value(value: Value, vm: &mut VM<'_, impl ResourceTracker>) -> Result<Self, FromValueFail> {
if value.is_str(vm.heap) {
Ok(Self(value))
} else {
value.drop_with_heap(vm);
Err(FromValueFail::WrongType)
}
}
fn drop_extracted(self, heap: &mut impl ContainsHeap) {
self.0.drop_with_heap(heap);
}
}
impl StrArg {
pub fn as_str<'a>(&'a self, vm: &'a VM<'_, impl ResourceTracker>) -> &'a str {
match self.0.to_str(vm) {
Ok(s) => s,
Err(_) => unreachable!("StrArg always holds a str"),
}
}
}
impl DropWithHeap for StrArg {
fn drop_with_heap<H: ContainsHeap>(self, heap: &mut H) {
self.0.drop_with_heap(heap);
}
}
impl<T: FromValue> FromValue for Option<T> {
const EXPECTED_TYPE_NAME: Option<&'static str> = T::EXPECTED_TYPE_NAME;
fn from_value(value: Value, vm: &mut VM<'_, impl ResourceTracker>) -> Result<Self, FromValueFail> {
T::from_value(value, vm).map(Some)
}
fn type_error(got: &str) -> RunError {
T::type_error(got)
}
fn drop_extracted(self, heap: &mut impl ContainsHeap) {
if let Some(inner) = self {
inner.drop_extracted(heap);
}
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct LaxBool(bool);
impl FromValue for LaxBool {
fn from_value(value: Value, vm: &mut VM<'_, impl ResourceTracker>) -> Result<Self, FromValueFail> {
let result = value.py_bool(vm);
value.drop_with_heap(vm);
Ok(Self(result))
}
}
impl LaxBool {
pub fn new(b: bool) -> Self {
Self(b)
}
pub fn bool(self) -> bool {
self.0
}
}