rustpython_vm/convert/
try_from.rs1use crate::{
2 Py, VirtualMachine,
3 builtins::PyFloat,
4 object::{AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult},
5};
6use malachite_bigint::Sign;
7use num_traits::ToPrimitive;
8
9pub trait TryFromObject: Sized {
14 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self>;
16}
17
18impl<T: for<'a> TryFromBorrowedObject<'a>> TryFromObject for T {
20 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
21 TryFromBorrowedObject::try_from_borrowed_object(vm, &obj)
22 }
23}
24
25impl PyObjectRef {
26 pub fn try_into_value<T>(self, vm: &VirtualMachine) -> PyResult<T>
27 where
28 T: TryFromObject,
29 {
30 T::try_from_object(vm, self)
31 }
32}
33
34impl PyObject {
35 pub fn try_to_value<'a, T>(&'a self, vm: &VirtualMachine) -> PyResult<T>
36 where
37 T: 'a + TryFromBorrowedObject<'a>,
38 {
39 T::try_from_borrowed_object(vm, self)
40 }
41
42 pub fn try_to_ref<'a, T>(&'a self, vm: &VirtualMachine) -> PyResult<&'a Py<T>>
43 where
44 T: 'a + PyPayload,
45 {
46 self.try_to_value::<&Py<T>>(vm)
47 }
48
49 pub fn try_value_with<T, F, R>(&self, f: F, vm: &VirtualMachine) -> PyResult<R>
50 where
51 T: PyPayload,
52 F: Fn(&T) -> PyResult<R>,
53 {
54 let class = T::class(&vm.ctx);
55 let py_ref = if self.fast_isinstance(class) {
56 self.downcast_ref()
57 .ok_or_else(|| vm.new_downcast_runtime_error(class, self))?
58 } else {
59 return Err(vm.new_downcast_type_error(class, self));
60 };
61 f(py_ref)
62 }
63}
64
65pub trait TryFromBorrowedObject<'a>: Sized
67where
68 Self: 'a,
69{
70 fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult<Self>;
72}
73
74impl<T> TryFromObject for PyRef<T>
75where
76 T: PyPayload,
77{
78 #[inline]
79 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
80 let class = T::class(&vm.ctx);
81 if obj.fast_isinstance(class) {
82 T::try_downcast_from(&obj, vm)?;
83 Ok(unsafe { obj.downcast_unchecked() })
84 } else {
85 Err(vm.new_downcast_type_error(class, &obj))
86 }
87 }
88}
89
90impl TryFromObject for PyObjectRef {
91 #[inline]
92 fn try_from_object(_vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
93 Ok(obj)
94 }
95}
96
97impl<T: TryFromObject> TryFromObject for Option<T> {
98 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
99 if vm.is_none(&obj) {
100 Ok(None)
101 } else {
102 T::try_from_object(vm, obj).map(Some)
103 }
104 }
105}
106
107impl<'a, T: 'a + TryFromObject> TryFromBorrowedObject<'a> for Vec<T> {
108 fn try_from_borrowed_object(vm: &VirtualMachine, value: &'a PyObject) -> PyResult<Self> {
109 vm.extract_elements_with(value, |obj| T::try_from_object(vm, obj))
110 }
111}
112
113impl<'a, T: PyPayload> TryFromBorrowedObject<'a> for &'a Py<T> {
114 fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult<Self> {
115 let class = T::class(&vm.ctx);
116 if obj.fast_isinstance(class) {
117 obj.downcast_ref()
118 .ok_or_else(|| vm.new_downcast_runtime_error(class, &obj))
119 } else {
120 Err(vm.new_downcast_type_error(class, &obj))
121 }
122 }
123}
124
125impl TryFromObject for core::time::Duration {
126 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
127 if let Some(float) = obj.downcast_ref::<PyFloat>() {
128 let f = float.to_f64();
129 if f.is_nan() {
130 return Err(vm.new_value_error("Invalid value NaN (not a number)"));
131 }
132 if f < 0.0 {
133 return Err(vm.new_value_error("negative duration"));
134 }
135 if !f.is_finite() || f > u64::MAX as f64 {
136 return Err(vm.new_overflow_error("timestamp too large to convert to C PyTime_t"));
137 }
138 let secs = f.trunc() as u64;
140 let frac = f.fract();
141 let nanos = (frac * 1_000_000_000.0).floor() as u32;
143 Ok(Self::new(secs, nanos))
144 } else if let Some(int) = obj.try_index_opt(vm) {
145 let int = int?;
146 let bigint = int.as_bigint();
147 if bigint.sign() == Sign::Minus {
148 return Err(vm.new_value_error("negative duration"));
149 }
150
151 let sec = bigint
152 .to_u64()
153 .ok_or_else(|| vm.new_value_error("value out of range"))?;
154 Ok(Self::from_secs(sec))
155 } else {
156 Err(vm.new_type_error(format!(
157 "expected an int or float for duration, got {}",
158 obj.class()
159 )))
160 }
161 }
162}