1use std::collections::hash_map::DefaultHasher;
5use std::hash::{Hash, Hasher};
6
7use pyo3::exceptions::{PyOverflowError, PyValueError};
8use pyo3::prelude::*;
9use pyo3::types::PyBytes;
10
11use synta::{BitString, Boolean, GeneralizedTime, Integer, OctetString, Real, UtcTime};
12
13#[pyclass(name = "Integer")]
15#[derive(Debug, Clone)]
16pub struct PyInteger {
17 pub(crate) inner: Integer,
18}
19
20#[pymethods]
21impl PyInteger {
22 #[new]
24 fn new(value: i64) -> Self {
25 Self {
26 inner: Integer::from_i64(value),
27 }
28 }
29
30 fn to_int(&self) -> PyResult<i64> {
32 self.inner
33 .as_i64()
34 .map_err(|_| PyOverflowError::new_err("Integer too large for i64"))
35 }
36
37 fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
39 PyBytes::new(py, self.inner.as_bytes())
40 }
41
42 #[staticmethod]
44 fn from_bytes(bytes: &[u8]) -> Self {
45 Self {
46 inner: Integer::from_bytes(bytes),
47 }
48 }
49
50 #[staticmethod]
52 fn from_u64(value: u64) -> Self {
53 Self {
54 inner: Integer::from_u64(value),
55 }
56 }
57
58 fn to_i128(&self) -> PyResult<i128> {
60 self.inner
61 .as_i128()
62 .map_err(|_| PyOverflowError::new_err("Integer too large for i128"))
63 }
64
65 fn __eq__(&self, other: &Self) -> bool {
66 self.inner.as_bytes() == other.inner.as_bytes()
69 }
70
71 fn __hash__(&self) -> u64 {
72 let mut h = DefaultHasher::new();
73 self.inner.as_bytes().hash(&mut h);
74 h.finish()
75 }
76
77 fn __repr__(&self) -> PyResult<String> {
78 match self.inner.as_i64() {
79 Ok(val) => Ok(format!("Integer({})", val)),
80 Err(_) => Ok(format!("Integer(<{} bytes>)", self.inner.as_bytes().len())),
81 }
82 }
83
84 fn __str__(&self) -> PyResult<String> {
85 match self.inner.as_i64() {
86 Ok(val) => Ok(val.to_string()),
87 Err(_) => Ok(format!("<integer {} bytes>", self.inner.as_bytes().len())),
88 }
89 }
90}
91
92#[pyclass(name = "OctetString")]
94#[derive(Debug, Clone)]
95pub struct PyOctetString {
96 pub(crate) inner: OctetString,
97}
98
99#[pymethods]
100impl PyOctetString {
101 #[new]
103 fn new(data: Vec<u8>) -> Self {
104 Self {
105 inner: OctetString::new(data),
106 }
107 }
108
109 fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
111 PyBytes::new(py, self.inner.as_bytes())
112 }
113
114 fn __len__(&self) -> usize {
116 self.inner.as_bytes().len()
117 }
118
119 fn __eq__(&self, other: &Self) -> bool {
120 self.inner == other.inner
121 }
122
123 fn __repr__(&self) -> String {
124 format!("OctetString(<{} bytes>)", self.inner.as_bytes().len())
125 }
126}
127
128#[pyclass(name = "BitString")]
130#[derive(Debug, Clone)]
131pub struct PyBitString {
132 pub(crate) inner: BitString,
133}
134
135#[pymethods]
136impl PyBitString {
137 #[new]
139 fn new(data: Vec<u8>, unused_bits: u8) -> PyResult<Self> {
140 if unused_bits > 7 {
141 return Err(PyValueError::new_err("unused_bits must be 0-7"));
142 }
143 let inner = BitString::new(data, unused_bits)
144 .map_err(|e| PyValueError::new_err(format!("Invalid BitString: {:?}", e)))?;
145 Ok(Self { inner })
146 }
147
148 fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
150 PyBytes::new(py, self.inner.as_bytes())
151 }
152
153 fn unused_bits(&self) -> u8 {
155 self.inner.unused_bits()
156 }
157
158 fn bit_len(&self) -> usize {
160 self.inner.bit_len()
161 }
162
163 fn __len__(&self) -> usize {
164 self.inner.bit_len()
165 }
166
167 fn __eq__(&self, other: &Self) -> bool {
168 self.inner == other.inner
169 }
170
171 fn __repr__(&self) -> String {
172 format!(
173 "BitString(<{} bits, {} unused>)",
174 self.inner.bit_len(),
175 self.inner.unused_bits()
176 )
177 }
178}
179
180#[pyclass(name = "Boolean")]
182#[derive(Debug, Clone, Copy)]
183pub struct PyBoolean {
184 pub(crate) inner: Boolean,
185}
186
187#[pymethods]
188impl PyBoolean {
189 #[new]
191 fn new(value: bool) -> Self {
192 Self {
193 inner: Boolean::new(value),
194 }
195 }
196
197 fn value(&self) -> bool {
199 self.inner.value()
200 }
201
202 fn __bool__(&self) -> bool {
203 self.inner.value()
204 }
205
206 fn __eq__(&self, other: &Self) -> bool {
207 self.inner.value() == other.inner.value()
208 }
209
210 fn __hash__(&self) -> isize {
211 self.inner.value() as isize
213 }
214
215 fn __repr__(&self) -> String {
216 format!("Boolean({})", self.inner.value())
217 }
218}
219
220#[pyclass(name = "UtcTime")]
222#[derive(Debug, Clone)]
223pub struct PyUtcTime {
224 pub(crate) inner: UtcTime,
225}
226
227#[pymethods]
228impl PyUtcTime {
229 #[new]
231 fn new(year: u16, month: u8, day: u8, hour: u8, minute: u8, second: u8) -> PyResult<Self> {
232 let inner = UtcTime::new(year, month, day, hour, minute, second)
233 .map_err(|e| PyValueError::new_err(format!("Invalid UTCTime: {:?}", e)))?;
234 Ok(Self { inner })
235 }
236
237 #[getter]
238 fn year(&self) -> u16 {
239 self.inner.year
240 }
241
242 #[getter]
243 fn month(&self) -> u8 {
244 self.inner.month
245 }
246
247 #[getter]
248 fn day(&self) -> u8 {
249 self.inner.day
250 }
251
252 #[getter]
253 fn hour(&self) -> u8 {
254 self.inner.hour
255 }
256
257 #[getter]
258 fn minute(&self) -> u8 {
259 self.inner.minute
260 }
261
262 #[getter]
263 fn second(&self) -> u8 {
264 self.inner.second
265 }
266
267 fn __str__(&self) -> String {
268 self.inner.to_string()
269 }
270
271 fn __repr__(&self) -> String {
272 format!("UtcTime('{}')", self.inner)
273 }
274}
275
276impl std::fmt::Display for PyUtcTime {
277 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
278 write!(f, "{}", self.inner)
279 }
280}
281
282#[pyclass(name = "GeneralizedTime")]
284#[derive(Debug, Clone)]
285pub struct PyGeneralizedTime {
286 pub(crate) inner: GeneralizedTime,
287}
288
289#[pymethods]
290impl PyGeneralizedTime {
291 #[new]
296 fn new(
297 year: u16,
298 month: u8,
299 day: u8,
300 hour: u8,
301 minute: u8,
302 second: u8,
303 milliseconds: Option<u16>,
304 ) -> PyResult<Self> {
305 let inner = GeneralizedTime::new(year, month, day, hour, minute, second, milliseconds)
306 .map_err(|e| PyValueError::new_err(format!("Invalid GeneralizedTime: {:?}", e)))?;
307 Ok(Self { inner })
308 }
309
310 #[getter]
311 fn year(&self) -> u16 {
312 self.inner.year
313 }
314
315 #[getter]
316 fn month(&self) -> u8 {
317 self.inner.month
318 }
319
320 #[getter]
321 fn day(&self) -> u8 {
322 self.inner.day
323 }
324
325 #[getter]
326 fn hour(&self) -> u8 {
327 self.inner.hour
328 }
329
330 #[getter]
331 fn minute(&self) -> u8 {
332 self.inner.minute
333 }
334
335 #[getter]
336 fn second(&self) -> u8 {
337 self.inner.second
338 }
339
340 #[getter]
341 fn milliseconds(&self) -> Option<u16> {
342 self.inner.milliseconds
343 }
344
345 fn __str__(&self) -> String {
346 self.inner.to_string()
347 }
348
349 fn __repr__(&self) -> String {
350 format!("GeneralizedTime('{}')", self.inner)
351 }
352}
353
354impl std::fmt::Display for PyGeneralizedTime {
355 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
356 write!(f, "{}", self.inner)
357 }
358}
359
360#[pyclass(name = "Real")]
362#[derive(Debug, Clone, Copy)]
363pub struct PyReal {
364 pub(crate) inner: Real,
365}
366
367#[pymethods]
368impl PyReal {
369 #[new]
371 fn new(value: f64) -> Self {
372 Self {
373 inner: Real::new(value),
374 }
375 }
376
377 fn value(&self) -> f64 {
379 self.inner.value()
380 }
381
382 fn is_infinite(&self) -> bool {
384 self.inner.value().is_infinite()
385 }
386
387 fn is_nan(&self) -> bool {
389 self.inner.value().is_nan()
390 }
391
392 fn is_finite(&self) -> bool {
394 self.inner.value().is_finite()
395 }
396
397 fn __float__(&self) -> f64 {
398 self.inner.value()
399 }
400
401 fn __repr__(&self) -> String {
402 format!("Real({})", self.inner.value())
403 }
404
405 fn __str__(&self) -> String {
406 self.inner.value().to_string()
407 }
408
409 fn __eq__(&self, other: &Self) -> bool {
410 self.inner.value() == other.inner.value()
412 }
413
414 fn __hash__(&self, py: Python<'_>) -> PyResult<isize> {
415 self.inner.value().into_pyobject(py)?.hash()
418 }
419}
420
421#[pyclass(name = "Null")]
423#[derive(Debug, Clone, Copy)]
424pub struct PyNull;
425
426#[pymethods]
427impl PyNull {
428 #[new]
429 fn new() -> Self {
430 Self
431 }
432
433 fn __repr__(&self) -> String {
434 "Null()".to_string()
435 }
436
437 fn __eq__(&self, _other: &Self) -> bool {
438 true
439 }
440
441 fn __hash__(&self) -> isize {
442 0
444 }
445}