rustpython_vm/convert/
try_from.rs1use crate::{
2 builtins::PyFloat,
3 object::{AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult},
4 Py, VirtualMachine,
5};
6use num_traits::ToPrimitive;
7
8pub trait TryFromObject: Sized {
13 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self>;
15}
16
17impl<T: for<'a> TryFromBorrowedObject<'a>> TryFromObject for T {
19 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
20 TryFromBorrowedObject::try_from_borrowed_object(vm, &obj)
21 }
22}
23
24impl PyObjectRef {
25 pub fn try_into_value<T>(self, vm: &VirtualMachine) -> PyResult<T>
26 where
27 T: TryFromObject,
28 {
29 T::try_from_object(vm, self)
30 }
31}
32
33impl PyObject {
34 pub fn try_to_value<'a, T>(&'a self, vm: &VirtualMachine) -> PyResult<T>
35 where
36 T: 'a + TryFromBorrowedObject<'a>,
37 {
38 T::try_from_borrowed_object(vm, self)
39 }
40
41 pub fn try_to_ref<'a, T>(&'a self, vm: &VirtualMachine) -> PyResult<&'a Py<T>>
42 where
43 T: 'a + PyPayload,
44 {
45 self.try_to_value::<&Py<T>>(vm)
46 }
47
48 pub fn try_value_with<T, F, R>(&self, f: F, vm: &VirtualMachine) -> PyResult<R>
49 where
50 T: PyPayload,
51 F: Fn(&T) -> PyResult<R>,
52 {
53 let class = T::class(&vm.ctx);
54 let py_ref = if self.fast_isinstance(class) {
55 self.downcast_ref()
56 .ok_or_else(|| vm.new_downcast_runtime_error(class, self))?
57 } else {
58 return Err(vm.new_downcast_type_error(class, self));
59 };
60 f(py_ref)
61 }
62}
63
64pub trait TryFromBorrowedObject<'a>: Sized
66where
67 Self: 'a,
68{
69 fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult<Self>;
71}
72
73impl<T> TryFromObject for PyRef<T>
74where
75 T: PyPayload,
76{
77 #[inline]
78 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
79 let class = T::class(&vm.ctx);
80 if obj.fast_isinstance(class) {
81 obj.downcast()
82 .map_err(|obj| vm.new_downcast_runtime_error(class, &obj))
83 } else {
84 Err(vm.new_downcast_type_error(class, &obj))
85 }
86 }
87}
88
89impl TryFromObject for PyObjectRef {
90 #[inline]
91 fn try_from_object(_vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
92 Ok(obj)
93 }
94}
95
96impl<T: TryFromObject> TryFromObject for Option<T> {
97 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
98 if vm.is_none(&obj) {
99 Ok(None)
100 } else {
101 T::try_from_object(vm, obj).map(Some)
102 }
103 }
104}
105
106impl<'a, T: 'a + TryFromObject> TryFromBorrowedObject<'a> for Vec<T> {
107 fn try_from_borrowed_object(vm: &VirtualMachine, value: &'a PyObject) -> PyResult<Self> {
108 vm.extract_elements_with(value, |obj| T::try_from_object(vm, obj))
109 }
110}
111
112impl<'a, T: PyPayload> TryFromBorrowedObject<'a> for &'a Py<T> {
113 fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult<Self> {
114 let class = T::class(&vm.ctx);
115 if obj.fast_isinstance(class) {
116 obj.downcast_ref()
117 .ok_or_else(|| vm.new_downcast_runtime_error(class, &obj))
118 } else {
119 Err(vm.new_downcast_type_error(class, &obj))
120 }
121 }
122}
123
124impl TryFromObject for std::time::Duration {
125 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
126 use std::time::Duration;
127 if let Some(float) = obj.payload::<PyFloat>() {
128 Ok(Duration::from_secs_f64(float.to_f64()))
129 } else if let Some(int) = obj.try_index_opt(vm) {
130 let sec = int?
131 .as_bigint()
132 .to_u64()
133 .ok_or_else(|| vm.new_value_error("value out of range".to_owned()))?;
134 Ok(Duration::from_secs(sec))
135 } else {
136 Err(vm.new_type_error(format!(
137 "expected an int or float for duration, got {}",
138 obj.class()
139 )))
140 }
141 }
142}