Skip to main content

rustpython_ruff_python_ast/
int.rs

1use std::fmt::Debug;
2use std::str::FromStr;
3
4/// A Python integer literal. Represents both small (fits in an `i64`) and large integers.
5#[derive(Clone, PartialEq, Eq, Hash)]
6#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))]
7pub struct Int(Number);
8
9impl FromStr for Int {
10    type Err = std::num::ParseIntError;
11
12    /// Parse an [`Int`] from a string.
13    fn from_str(s: &str) -> Result<Self, Self::Err> {
14        match s.parse::<u64>() {
15            Ok(value) => Ok(Int::small(value)),
16            Err(err) => {
17                if matches!(
18                    err.kind(),
19                    std::num::IntErrorKind::PosOverflow | std::num::IntErrorKind::NegOverflow
20                ) {
21                    Ok(Int::big(s))
22                } else {
23                    Err(err)
24                }
25            }
26        }
27    }
28}
29
30impl Int {
31    pub const ZERO: Int = Int(Number::Small(0));
32    pub const ONE: Int = Int(Number::Small(1));
33
34    /// Create an [`Int`] to represent a value that can be represented as an `i64`.
35    fn small(value: u64) -> Self {
36        Self(Number::Small(value))
37    }
38
39    /// Create an [`Int`] to represent a value that cannot be represented as an `i64`.
40    fn big(value: impl Into<Box<str>>) -> Self {
41        Self(Number::Big(value.into()))
42    }
43
44    /// Parse an [`Int`] from a string with a given radix, like `0x95D`.
45    ///
46    /// Takes, as input, the numerical portion (`95D`), the parsed base (`16`), and the entire
47    /// token (`0x95D`).
48    pub fn from_str_radix(
49        number: &str,
50        radix: u32,
51        token: &str,
52    ) -> Result<Self, std::num::ParseIntError> {
53        match u64::from_str_radix(number, radix) {
54            Ok(value) => Ok(Int::small(value)),
55            Err(err) => {
56                if matches!(
57                    err.kind(),
58                    std::num::IntErrorKind::PosOverflow | std::num::IntErrorKind::NegOverflow
59                ) {
60                    Ok(Int::big(token))
61                } else {
62                    Err(err)
63                }
64            }
65        }
66    }
67
68    /// Return the [`Int`] as an u8, if it can be represented as that data type.
69    pub fn as_u8(&self) -> Option<u8> {
70        match &self.0 {
71            Number::Small(small) => u8::try_from(*small).ok(),
72            Number::Big(_) => None,
73        }
74    }
75
76    /// Return the [`Int`] as an u16, if it can be represented as that data type.
77    pub fn as_u16(&self) -> Option<u16> {
78        match &self.0 {
79            Number::Small(small) => u16::try_from(*small).ok(),
80            Number::Big(_) => None,
81        }
82    }
83
84    /// Return the [`Int`] as an u32, if it can be represented as that data type.
85    pub fn as_u32(&self) -> Option<u32> {
86        match &self.0 {
87            Number::Small(small) => u32::try_from(*small).ok(),
88            Number::Big(_) => None,
89        }
90    }
91
92    /// Return the [`Int`] as an u64, if it can be represented as that data type.
93    pub const fn as_u64(&self) -> Option<u64> {
94        match &self.0 {
95            Number::Small(small) => Some(*small),
96            Number::Big(_) => None,
97        }
98    }
99
100    /// Return the [`Int`] as an usize, if it can be represented as that data type.
101    pub fn as_usize(&self) -> Option<usize> {
102        match &self.0 {
103            Number::Small(small) => usize::try_from(*small).ok(),
104            Number::Big(_) => None,
105        }
106    }
107
108    /// Return the [`Int`] as an i8, if it can be represented as that data type.
109    pub fn as_i8(&self) -> Option<i8> {
110        match &self.0 {
111            Number::Small(small) => i8::try_from(*small).ok(),
112            Number::Big(_) => None,
113        }
114    }
115
116    /// Return the [`Int`] as an i16, if it can be represented as that data type.
117    pub fn as_i16(&self) -> Option<i16> {
118        match &self.0 {
119            Number::Small(small) => i16::try_from(*small).ok(),
120            Number::Big(_) => None,
121        }
122    }
123
124    /// Return the [`Int`] as an i32, if it can be represented as that data type.
125    pub fn as_i32(&self) -> Option<i32> {
126        match &self.0 {
127            Number::Small(small) => i32::try_from(*small).ok(),
128            Number::Big(_) => None,
129        }
130    }
131
132    /// Return the [`Int`] as an i64, if it can be represented as that data type.
133    pub fn as_i64(&self) -> Option<i64> {
134        match &self.0 {
135            Number::Small(small) => i64::try_from(*small).ok(),
136            Number::Big(_) => None,
137        }
138    }
139}
140
141impl std::fmt::Display for Int {
142    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143        write!(f, "{}", self.0)
144    }
145}
146
147impl Debug for Int {
148    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149        std::fmt::Display::fmt(self, f)
150    }
151}
152
153impl PartialEq<u8> for Int {
154    fn eq(&self, other: &u8) -> bool {
155        self.as_u8() == Some(*other)
156    }
157}
158
159impl PartialEq<u16> for Int {
160    fn eq(&self, other: &u16) -> bool {
161        self.as_u16() == Some(*other)
162    }
163}
164
165impl PartialEq<u32> for Int {
166    fn eq(&self, other: &u32) -> bool {
167        self.as_u32() == Some(*other)
168    }
169}
170
171impl PartialEq<i8> for Int {
172    fn eq(&self, other: &i8) -> bool {
173        self.as_i8() == Some(*other)
174    }
175}
176
177impl PartialEq<i16> for Int {
178    fn eq(&self, other: &i16) -> bool {
179        self.as_i16() == Some(*other)
180    }
181}
182
183impl PartialEq<i32> for Int {
184    fn eq(&self, other: &i32) -> bool {
185        self.as_i32() == Some(*other)
186    }
187}
188
189impl PartialEq<i64> for Int {
190    fn eq(&self, other: &i64) -> bool {
191        self.as_i64() == Some(*other)
192    }
193}
194
195impl From<u8> for Int {
196    fn from(value: u8) -> Self {
197        Self::small(u64::from(value))
198    }
199}
200
201impl From<u16> for Int {
202    fn from(value: u16) -> Self {
203        Self::small(u64::from(value))
204    }
205}
206
207impl From<u32> for Int {
208    fn from(value: u32) -> Self {
209        Self::small(u64::from(value))
210    }
211}
212
213impl From<u64> for Int {
214    fn from(value: u64) -> Self {
215        Self::small(value)
216    }
217}
218
219#[derive(Debug, Clone, PartialEq, Eq, Hash)]
220#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))]
221enum Number {
222    /// A "small" number that can be represented as an `u64`.
223    Small(u64),
224    /// A "large" number that cannot be represented as an `u64`.
225    Big(Box<str>),
226}
227
228impl std::fmt::Display for Number {
229    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
230        match self {
231            Number::Small(value) => write!(f, "{value}"),
232            Number::Big(value) => write!(f, "{value}"),
233        }
234    }
235}