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::{
12 BitString, Boolean, FromDer, GeneralizedTime, Integer, Null, OctetString, Real, ToDer, UtcTime,
13};
14
15use crate::error::SyntaErr;
16
17#[pyclass(name = "Integer")]
19#[derive(Debug, Clone)]
20pub struct PyInteger {
21 pub(crate) inner: Integer,
22}
23
24#[pymethods]
25impl PyInteger {
26 #[new]
28 fn new(value: i64) -> Self {
29 Self {
30 inner: Integer::from_i64(value),
31 }
32 }
33
34 fn to_int(&self) -> PyResult<i64> {
36 self.inner
37 .as_i64()
38 .map_err(|_| PyOverflowError::new_err("Integer too large for i64"))
39 }
40
41 fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
43 PyBytes::new(py, self.inner.as_bytes())
44 }
45
46 #[staticmethod]
48 fn from_bytes(bytes: &[u8]) -> Self {
49 Self {
50 inner: Integer::from_bytes(bytes),
51 }
52 }
53
54 #[staticmethod]
56 fn from_u64(value: u64) -> Self {
57 Self {
58 inner: Integer::from_u64(value),
59 }
60 }
61
62 fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
64 Ok(PyBytes::new(py, &self.inner.to_der().map_err(SyntaErr)?))
65 }
66
67 #[staticmethod]
71 fn from_der(data: &[u8]) -> PyResult<Self> {
72 let inner = Integer::from_der(data).map_err(SyntaErr)?;
73 Ok(Self { inner })
74 }
75
76 fn to_i128(&self) -> PyResult<i128> {
78 self.inner
79 .as_i128()
80 .map_err(|_| PyOverflowError::new_err("Integer too large for i128"))
81 }
82
83 fn __eq__(&self, other: &Self) -> bool {
84 self.inner.as_bytes() == other.inner.as_bytes()
87 }
88
89 fn __hash__(&self) -> u64 {
90 let mut h = DefaultHasher::new();
91 self.inner.as_bytes().hash(&mut h);
92 h.finish()
93 }
94
95 fn __repr__(&self) -> PyResult<String> {
96 match self.inner.as_i64() {
97 Ok(val) => Ok(format!("Integer({})", val)),
98 Err(_) => Ok(format!("Integer(<{} bytes>)", self.inner.as_bytes().len())),
99 }
100 }
101
102 fn __str__(&self) -> PyResult<String> {
103 match self.inner.as_i64() {
104 Ok(val) => Ok(val.to_string()),
105 Err(_) => Ok(format!("<integer {} bytes>", self.inner.as_bytes().len())),
106 }
107 }
108}
109
110#[pyclass(name = "OctetString")]
112#[derive(Debug, Clone)]
113pub struct PyOctetString {
114 pub(crate) inner: OctetString,
115}
116
117#[pymethods]
118impl PyOctetString {
119 #[new]
121 fn new(data: Vec<u8>) -> Self {
122 Self {
123 inner: OctetString::new(data),
124 }
125 }
126
127 fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
129 PyBytes::new(py, self.inner.as_bytes())
130 }
131
132 fn __len__(&self) -> usize {
134 self.inner.as_bytes().len()
135 }
136
137 fn __eq__(&self, other: &Self) -> bool {
138 self.inner == other.inner
139 }
140
141 fn __repr__(&self) -> String {
142 format!("OctetString(<{} bytes>)", self.inner.as_bytes().len())
143 }
144
145 fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
147 Ok(PyBytes::new(py, &self.inner.to_der().map_err(SyntaErr)?))
148 }
149
150 #[staticmethod]
154 fn from_der(data: &[u8]) -> PyResult<Self> {
155 let inner = OctetString::from_der(data).map_err(SyntaErr)?;
156 Ok(Self { inner })
157 }
158}
159
160#[pyclass(name = "BitString")]
162#[derive(Debug, Clone)]
163pub struct PyBitString {
164 pub(crate) inner: BitString,
165}
166
167#[pymethods]
168impl PyBitString {
169 #[new]
171 fn new(data: Vec<u8>, unused_bits: u8) -> PyResult<Self> {
172 if unused_bits > 7 {
173 return Err(PyValueError::new_err("unused_bits must be 0-7"));
174 }
175 let inner = BitString::new(data, unused_bits)
176 .map_err(|e| PyValueError::new_err(format!("Invalid BitString: {:?}", e)))?;
177 Ok(Self { inner })
178 }
179
180 fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
182 PyBytes::new(py, self.inner.as_bytes())
183 }
184
185 fn unused_bits(&self) -> u8 {
187 self.inner.unused_bits()
188 }
189
190 fn bit_len(&self) -> usize {
192 self.inner.bit_len()
193 }
194
195 fn __len__(&self) -> usize {
196 self.inner.bit_len()
197 }
198
199 fn __eq__(&self, other: &Self) -> bool {
200 self.inner == other.inner
201 }
202
203 fn __repr__(&self) -> String {
204 format!(
205 "BitString(<{} bits, {} unused>)",
206 self.inner.bit_len(),
207 self.inner.unused_bits()
208 )
209 }
210
211 fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
213 Ok(PyBytes::new(py, &self.inner.to_der().map_err(SyntaErr)?))
214 }
215
216 #[staticmethod]
220 fn from_der(data: &[u8]) -> PyResult<Self> {
221 let inner = BitString::from_der(data).map_err(SyntaErr)?;
222 Ok(Self { inner })
223 }
224}
225
226#[pyclass(name = "Boolean")]
228#[derive(Debug, Clone, Copy)]
229pub struct PyBoolean {
230 pub(crate) inner: Boolean,
231}
232
233#[pymethods]
234impl PyBoolean {
235 #[new]
237 fn new(value: bool) -> Self {
238 Self {
239 inner: Boolean::new(value),
240 }
241 }
242
243 fn value(&self) -> bool {
245 self.inner.value()
246 }
247
248 fn __bool__(&self) -> bool {
249 self.inner.value()
250 }
251
252 fn __eq__(&self, other: &Self) -> bool {
253 self.inner.value() == other.inner.value()
254 }
255
256 fn __hash__(&self) -> isize {
257 self.inner.value() as isize
259 }
260
261 fn __repr__(&self) -> String {
262 format!("Boolean({})", self.inner.value())
263 }
264
265 #[allow(clippy::wrong_self_convention)]
267 fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
268 Ok(PyBytes::new(py, &self.inner.to_der().map_err(SyntaErr)?))
269 }
270
271 #[staticmethod]
275 fn from_der(data: &[u8]) -> PyResult<Self> {
276 let inner = Boolean::from_der(data).map_err(SyntaErr)?;
277 Ok(Self { inner })
278 }
279}
280
281#[pyclass(name = "UtcTime")]
283#[derive(Debug, Clone)]
284pub struct PyUtcTime {
285 pub(crate) inner: UtcTime,
286}
287
288#[pymethods]
289impl PyUtcTime {
290 #[new]
292 fn new(year: u16, month: u8, day: u8, hour: u8, minute: u8, second: u8) -> PyResult<Self> {
293 let inner = UtcTime::new(year, month, day, hour, minute, second)
294 .map_err(|e| PyValueError::new_err(format!("Invalid UTCTime: {:?}", e)))?;
295 Ok(Self { inner })
296 }
297
298 #[getter]
300 fn year(&self) -> u16 {
301 self.inner.year
302 }
303
304 #[getter]
306 fn month(&self) -> u8 {
307 self.inner.month
308 }
309
310 #[getter]
312 fn day(&self) -> u8 {
313 self.inner.day
314 }
315
316 #[getter]
318 fn hour(&self) -> u8 {
319 self.inner.hour
320 }
321
322 #[getter]
324 fn minute(&self) -> u8 {
325 self.inner.minute
326 }
327
328 #[getter]
330 fn second(&self) -> u8 {
331 self.inner.second
332 }
333
334 fn __str__(&self) -> String {
335 self.inner.to_string()
336 }
337
338 fn __repr__(&self) -> String {
339 format!("UtcTime('{}')", self.inner)
340 }
341
342 fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
344 Ok(PyBytes::new(py, &self.inner.to_der().map_err(SyntaErr)?))
345 }
346
347 #[staticmethod]
351 fn from_der(data: &[u8]) -> PyResult<Self> {
352 let inner = UtcTime::from_der(data).map_err(SyntaErr)?;
353 Ok(Self { inner })
354 }
355}
356
357impl std::fmt::Display for PyUtcTime {
358 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
359 write!(f, "{}", self.inner)
360 }
361}
362
363#[pyclass(name = "GeneralizedTime")]
365#[derive(Debug, Clone)]
366pub struct PyGeneralizedTime {
367 pub(crate) inner: GeneralizedTime,
368}
369
370#[pymethods]
371impl PyGeneralizedTime {
372 #[new]
377 fn new(
378 year: u16,
379 month: u8,
380 day: u8,
381 hour: u8,
382 minute: u8,
383 second: u8,
384 milliseconds: Option<u16>,
385 ) -> PyResult<Self> {
386 let inner = GeneralizedTime::new(year, month, day, hour, minute, second, milliseconds)
387 .map_err(|e| PyValueError::new_err(format!("Invalid GeneralizedTime: {:?}", e)))?;
388 Ok(Self { inner })
389 }
390
391 #[getter]
393 fn year(&self) -> u16 {
394 self.inner.year
395 }
396
397 #[getter]
399 fn month(&self) -> u8 {
400 self.inner.month
401 }
402
403 #[getter]
405 fn day(&self) -> u8 {
406 self.inner.day
407 }
408
409 #[getter]
411 fn hour(&self) -> u8 {
412 self.inner.hour
413 }
414
415 #[getter]
417 fn minute(&self) -> u8 {
418 self.inner.minute
419 }
420
421 #[getter]
423 fn second(&self) -> u8 {
424 self.inner.second
425 }
426
427 #[getter]
429 fn milliseconds(&self) -> Option<u16> {
430 self.inner.milliseconds
431 }
432
433 fn __str__(&self) -> String {
434 self.inner.to_string()
435 }
436
437 fn __repr__(&self) -> String {
438 format!("GeneralizedTime('{}')", self.inner)
439 }
440
441 fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
443 Ok(PyBytes::new(py, &self.inner.to_der().map_err(SyntaErr)?))
444 }
445
446 #[staticmethod]
450 fn from_der(data: &[u8]) -> PyResult<Self> {
451 let inner = GeneralizedTime::from_der(data).map_err(SyntaErr)?;
452 Ok(Self { inner })
453 }
454}
455
456impl std::fmt::Display for PyGeneralizedTime {
457 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
458 write!(f, "{}", self.inner)
459 }
460}
461
462#[pyclass(name = "Real")]
464#[derive(Debug, Clone, Copy)]
465pub struct PyReal {
466 pub(crate) inner: Real,
467}
468
469#[pymethods]
470impl PyReal {
471 #[new]
473 fn new(value: f64) -> Self {
474 Self {
475 inner: Real::new(value),
476 }
477 }
478
479 fn value(&self) -> f64 {
481 self.inner.value()
482 }
483
484 fn is_infinite(&self) -> bool {
486 self.inner.value().is_infinite()
487 }
488
489 fn is_nan(&self) -> bool {
491 self.inner.value().is_nan()
492 }
493
494 fn is_finite(&self) -> bool {
496 self.inner.value().is_finite()
497 }
498
499 fn __float__(&self) -> f64 {
500 self.inner.value()
501 }
502
503 fn __repr__(&self) -> String {
504 format!("Real({})", self.inner.value())
505 }
506
507 fn __str__(&self) -> String {
508 self.inner.value().to_string()
509 }
510
511 fn __eq__(&self, other: &Self) -> bool {
512 self.inner.value() == other.inner.value()
514 }
515
516 fn __hash__(&self, py: Python<'_>) -> PyResult<isize> {
517 self.inner.value().into_pyobject(py)?.hash()
520 }
521
522 #[allow(clippy::wrong_self_convention)]
524 fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
525 Ok(PyBytes::new(py, &self.inner.to_der().map_err(SyntaErr)?))
526 }
527
528 #[staticmethod]
532 fn from_der(data: &[u8]) -> PyResult<Self> {
533 let inner = Real::from_der(data).map_err(SyntaErr)?;
534 Ok(Self { inner })
535 }
536}
537
538#[pyclass(name = "Null")]
540#[derive(Debug, Clone, Copy)]
541pub struct PyNull;
542
543#[pymethods]
544impl PyNull {
545 #[new]
547 fn new() -> Self {
548 Self
549 }
550
551 fn __repr__(&self) -> String {
552 "Null()".to_string()
553 }
554
555 fn __eq__(&self, _other: &Self) -> bool {
556 true
557 }
558
559 fn __hash__(&self) -> isize {
560 0
562 }
563
564 #[allow(clippy::wrong_self_convention)]
566 fn to_der<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
567 Ok(PyBytes::new(py, &Null.to_der().map_err(SyntaErr)?))
568 }
569
570 #[staticmethod]
574 fn from_der(data: &[u8]) -> PyResult<Self> {
575 Null::from_der(data).map_err(SyntaErr)?;
576 Ok(Self)
577 }
578}