Skip to main content

rustpython_vm/function/
number.rs

1use super::argument::OptionalArg;
2use crate::{
3    AsObject, Py, PyObjectRef, PyResult, TryFromObject, VirtualMachine,
4    builtins::{PyInt, PyIntRef},
5};
6use core::ops::Deref;
7use malachite_bigint::BigInt;
8use num_complex::Complex64;
9use num_traits::PrimInt;
10
11/// A Python complex-like object.
12///
13/// `ArgIntoComplex` implements `FromArgs` so that a built-in function can accept
14/// any object that can be transformed into a complex.
15///
16/// If the object is not a Python complex object but has a `__complex__()`
17/// method, this method will first be called to convert the object into a float.
18/// If `__complex__()` is not defined then it falls back to `__float__()`. If
19/// `__float__()` is not defined it falls back to `__index__()`.
20#[derive(Debug, PartialEq)]
21#[repr(transparent)]
22pub struct ArgIntoComplex {
23    value: Complex64,
24}
25
26impl ArgIntoComplex {
27    #[inline]
28    #[must_use]
29    pub fn into_complex(self) -> Complex64 {
30        self.value
31    }
32}
33
34impl From<ArgIntoComplex> for Complex64 {
35    fn from(arg: ArgIntoComplex) -> Self {
36        arg.value
37    }
38}
39
40impl TryFromObject for ArgIntoComplex {
41    // Equivalent to PyComplex_AsCComplex
42    fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
43        // We do not care if it was already a complex.
44        let (value, _) = obj.try_complex(vm)?.ok_or_else(|| {
45            vm.new_type_error(format!("must be real number, not {}", obj.class().name()))
46        })?;
47        Ok(Self { value })
48    }
49}
50
51/// A Python float-like object.
52///
53/// `ArgIntoFloat` implements `FromArgs` so that a built-in function can accept
54/// any object that can be transformed into a float.
55///
56/// If the object is not a Python floating point object but has a `__float__()`
57/// method, this method will first be called to convert the object into a float.
58/// If `__float__()` is not defined then it falls back to `__index__()`.
59#[derive(Debug, PartialEq)]
60#[repr(transparent)]
61pub struct ArgIntoFloat {
62    value: f64,
63}
64
65impl ArgIntoFloat {
66    #[inline]
67    #[must_use]
68    pub fn into_float(self) -> f64 {
69        self.value
70    }
71
72    #[must_use]
73    pub fn vec_into_f64(v: Vec<Self>) -> Vec<f64> {
74        // TODO: Vec::into_raw_parts once stabilized
75        let mut v = core::mem::ManuallyDrop::new(v);
76        let (p, l, c) = (v.as_mut_ptr(), v.len(), v.capacity());
77        // SAFETY: IntoPyFloat is repr(transparent) over f64
78        unsafe { Vec::from_raw_parts(p.cast(), l, c) }
79    }
80}
81
82impl From<ArgIntoFloat> for f64 {
83    fn from(arg: ArgIntoFloat) -> Self {
84        arg.value
85    }
86}
87
88impl TryFromObject for ArgIntoFloat {
89    // Equivalent to PyFloat_AsDouble.
90    fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
91        let value = obj.try_float(vm)?.to_f64();
92        Ok(Self { value })
93    }
94}
95
96/// A Python bool-like object.
97///
98/// `ArgIntoBool` implements `FromArgs` so that a built-in function can accept
99/// any object that can be transformed into a boolean.
100///
101/// By default an object is considered true unless its class defines either a
102/// `__bool__()` method that returns False or a `__len__()` method that returns
103/// zero, when called with the object.
104#[derive(Debug, Default, PartialEq, Eq)]
105pub struct ArgIntoBool {
106    value: bool,
107}
108
109impl ArgIntoBool {
110    pub const TRUE: Self = Self { value: true };
111    pub const FALSE: Self = Self { value: false };
112
113    #[inline]
114    #[must_use]
115    pub fn into_bool(self) -> bool {
116        self.value
117    }
118
119    #[must_use]
120    pub const fn py_default(&self) -> super::DefaultRepr {
121        super::DefaultRepr::Bool(self.value)
122    }
123}
124
125impl From<ArgIntoBool> for bool {
126    fn from(arg: ArgIntoBool) -> Self {
127        arg.value
128    }
129}
130
131impl TryFromObject for ArgIntoBool {
132    fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
133        Ok(Self {
134            value: obj.try_to_bool(vm)?,
135        })
136    }
137}
138
139// Implement ArgIndex to separate between "true" int and int generated by index
140#[derive(Debug, Traverse)]
141#[repr(transparent)]
142pub struct ArgIndex {
143    value: PyIntRef,
144}
145
146impl ArgIndex {
147    #[inline]
148    #[must_use]
149    pub fn into_int_ref(self) -> PyIntRef {
150        self.value
151    }
152}
153
154impl AsRef<Py<PyInt>> for ArgIndex {
155    fn as_ref(&self) -> &Py<PyInt> {
156        &self.value
157    }
158}
159
160impl From<ArgIndex> for PyIntRef {
161    fn from(arg: ArgIndex) -> Self {
162        arg.value
163    }
164}
165
166impl TryFromObject for ArgIndex {
167    fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
168        Ok(Self {
169            value: obj.try_index(vm)?,
170        })
171    }
172}
173
174/// A signed size or index argument (`Py_ssize_t`).
175pub type PySsize = isize;
176/// An unsigned size argument (`size_t`).
177pub type PySize = usize;
178
179/// An `int` (or subclass, including `bool`) converted to a Rust primitive.
180///
181/// Unlike the primitive `TryFromObject` impls, this never calls `__index__`.
182#[derive(Debug, Copy, Clone)]
183#[repr(transparent)]
184pub struct ArgStrictInt<T> {
185    pub value: T,
186}
187
188impl<T> ArgStrictInt<T> {
189    #[inline]
190    #[must_use]
191    pub fn into_primitive(self) -> T {
192        self.value
193    }
194}
195
196impl<T> OptionalArg<ArgStrictInt<T>> {
197    pub fn into_primitive(self) -> OptionalArg<T> {
198        self.map(|x| x.value)
199    }
200}
201
202impl<T> Deref for ArgStrictInt<T> {
203    type Target = T;
204
205    fn deref(&self) -> &Self::Target {
206        &self.value
207    }
208}
209
210impl<T> TryFromObject for ArgStrictInt<T>
211where
212    T: PrimInt + for<'a> TryFrom<&'a BigInt>,
213{
214    fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
215        // Same check the primitive conversions used before they grew `__index__`.
216        let value = obj.try_value_with(|int: &Py<PyInt>| int.try_to_primitive(vm), vm)?;
217        Ok(Self { value })
218    }
219}