1use std::collections::hash_map::DefaultHasher;
4use std::hash::{Hash, Hasher};
5use std::str::FromStr;
6use std::sync::OnceLock;
7
8use pyo3::exceptions::{PyOverflowError, PyValueError};
9use pyo3::prelude::*;
10use pyo3::types::{PyBytes, PyString, PyTuple};
11
12use synta::{
13 BitString, BmpString, Boolean, GeneralString, GeneralizedTime, IA5String, Integer,
14 NumericString, ObjectIdentifier, OctetString, PrintableString, Real, TeletexString,
15 UniversalString, UtcTime, Utf8String, VisibleString,
16};
17
18#[pyclass(frozen, name = "ObjectIdentifier")]
31#[derive(Debug, Clone)]
32pub struct PyObjectIdentifier {
33 pub(crate) inner: ObjectIdentifier,
34 dotted_cache: OnceLock<String>,
37}
38
39impl PyObjectIdentifier {
40 pub fn from_oid(inner: ObjectIdentifier) -> Self {
43 Self {
44 inner,
45 dotted_cache: OnceLock::new(),
46 }
47 }
48}
49
50#[pymethods]
51impl PyObjectIdentifier {
52 #[new]
54 fn new(oid_str: &str) -> PyResult<Self> {
55 let inner = ObjectIdentifier::from_str(oid_str)
56 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("Invalid OID: {e:?}")))?;
57 Ok(Self {
58 inner,
59 dotted_cache: OnceLock::new(),
60 })
61 }
62
63 #[staticmethod]
65 fn from_components(components: Vec<u32>) -> PyResult<Self> {
66 let inner = ObjectIdentifier::new(&components)
67 .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("Invalid OID: {e:?}")))?;
68 Ok(Self {
69 inner,
70 dotted_cache: OnceLock::new(),
71 })
72 }
73
74 #[staticmethod]
87 fn from_der_value(data: &[u8]) -> PyResult<Self> {
88 let inner = ObjectIdentifier::from_content_bytes(data).map_err(|e| {
89 pyo3::exceptions::PyValueError::new_err(format!("Invalid OID content: {e:?}"))
90 })?;
91 Ok(Self {
92 inner,
93 dotted_cache: OnceLock::new(),
94 })
95 }
96
97 fn components<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
99 PyTuple::new(py, self.inner.components())
100 }
101
102 fn __str__(&self) -> &str {
103 self.dotted_cache.get_or_init(|| self.inner.to_string())
104 }
105
106 fn __repr__(&self) -> String {
107 format!(
108 "ObjectIdentifier('{}')",
109 self.dotted_cache.get_or_init(|| self.inner.to_string())
110 )
111 }
112
113 fn __eq__(&self, other: &Bound<'_, PyAny>) -> bool {
116 if let Ok(other_oid) = other.extract::<PyRef<PyObjectIdentifier>>() {
117 return self.inner == other_oid.inner;
118 }
119 if let Ok(s) = other.extract::<String>() {
120 return self.dotted_cache.get_or_init(|| self.inner.to_string()) == &s;
121 }
122 false
123 }
124
125 fn __hash__(&self, py: Python<'_>) -> PyResult<isize> {
129 PyString::new(py, self.dotted_cache.get_or_init(|| self.inner.to_string())).hash()
130 }
131}
132
133#[pyclass(name = "Integer")]
135#[derive(Debug, Clone)]
136pub struct PyInteger {
137 pub(crate) inner: Integer,
138}
139
140#[pymethods]
141impl PyInteger {
142 #[new]
144 fn new(value: i64) -> Self {
145 Self {
146 inner: Integer::from_i64(value),
147 }
148 }
149
150 fn to_int(&self) -> PyResult<i64> {
152 self.inner
153 .as_i64()
154 .map_err(|_| PyOverflowError::new_err("Integer too large for i64"))
155 }
156
157 fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
159 PyBytes::new(py, self.inner.as_bytes())
160 }
161
162 #[staticmethod]
164 fn from_bytes(bytes: &[u8]) -> Self {
165 Self {
166 inner: Integer::from_bytes(bytes),
167 }
168 }
169
170 #[staticmethod]
172 fn from_u64(value: u64) -> Self {
173 Self {
174 inner: Integer::from_u64(value),
175 }
176 }
177
178 fn to_i128(&self) -> PyResult<i128> {
180 self.inner
181 .as_i128()
182 .map_err(|_| PyOverflowError::new_err("Integer too large for i128"))
183 }
184
185 fn __eq__(&self, other: &Self) -> bool {
186 self.inner.as_bytes() == other.inner.as_bytes()
189 }
190
191 fn __hash__(&self) -> u64 {
192 let mut h = DefaultHasher::new();
193 self.inner.as_bytes().hash(&mut h);
194 h.finish()
195 }
196
197 fn __repr__(&self) -> PyResult<String> {
198 match self.inner.as_i64() {
199 Ok(val) => Ok(format!("Integer({})", val)),
200 Err(_) => Ok(format!("Integer(<{} bytes>)", self.inner.as_bytes().len())),
201 }
202 }
203
204 fn __str__(&self) -> PyResult<String> {
205 match self.inner.as_i64() {
206 Ok(val) => Ok(val.to_string()),
207 Err(_) => Ok(format!("<integer {} bytes>", self.inner.as_bytes().len())),
208 }
209 }
210}
211
212#[pyclass(name = "OctetString")]
214#[derive(Debug, Clone)]
215pub struct PyOctetString {
216 pub(crate) inner: OctetString,
217}
218
219#[pymethods]
220impl PyOctetString {
221 #[new]
223 fn new(data: Vec<u8>) -> Self {
224 Self {
225 inner: OctetString::new(data),
226 }
227 }
228
229 fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
231 PyBytes::new(py, self.inner.as_bytes())
232 }
233
234 fn __len__(&self) -> usize {
236 self.inner.as_bytes().len()
237 }
238
239 fn __eq__(&self, other: &Self) -> bool {
240 self.inner == other.inner
241 }
242
243 fn __repr__(&self) -> String {
244 format!("OctetString(<{} bytes>)", self.inner.as_bytes().len())
245 }
246}
247
248#[pyclass(name = "BitString")]
250#[derive(Debug, Clone)]
251pub struct PyBitString {
252 pub(crate) inner: BitString,
253}
254
255#[pymethods]
256impl PyBitString {
257 #[new]
259 fn new(data: Vec<u8>, unused_bits: u8) -> PyResult<Self> {
260 if unused_bits > 7 {
261 return Err(PyValueError::new_err("unused_bits must be 0-7"));
262 }
263 let inner = BitString::new(data, unused_bits)
264 .map_err(|e| PyValueError::new_err(format!("Invalid BitString: {:?}", e)))?;
265 Ok(Self { inner })
266 }
267
268 fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
270 PyBytes::new(py, self.inner.as_bytes())
271 }
272
273 fn unused_bits(&self) -> u8 {
275 self.inner.unused_bits()
276 }
277
278 fn bit_len(&self) -> usize {
280 self.inner.bit_len()
281 }
282
283 fn __len__(&self) -> usize {
284 self.inner.bit_len()
285 }
286
287 fn __eq__(&self, other: &Self) -> bool {
288 self.inner == other.inner
289 }
290
291 fn __repr__(&self) -> String {
292 format!(
293 "BitString(<{} bits, {} unused>)",
294 self.inner.bit_len(),
295 self.inner.unused_bits()
296 )
297 }
298}
299
300#[pyclass(name = "Boolean")]
302#[derive(Debug, Clone, Copy)]
303pub struct PyBoolean {
304 pub(crate) inner: Boolean,
305}
306
307#[pymethods]
308impl PyBoolean {
309 #[new]
311 fn new(value: bool) -> Self {
312 Self {
313 inner: Boolean::new(value),
314 }
315 }
316
317 fn value(&self) -> bool {
319 self.inner.value()
320 }
321
322 fn __bool__(&self) -> bool {
323 self.inner.value()
324 }
325
326 fn __eq__(&self, other: &Self) -> bool {
327 self.inner.value() == other.inner.value()
328 }
329
330 fn __hash__(&self) -> isize {
331 self.inner.value() as isize
333 }
334
335 fn __repr__(&self) -> String {
336 format!("Boolean({})", self.inner.value())
337 }
338}
339
340#[pyclass(name = "UtcTime")]
342#[derive(Debug, Clone)]
343pub struct PyUtcTime {
344 pub(crate) inner: UtcTime,
345}
346
347#[pymethods]
348impl PyUtcTime {
349 #[new]
351 fn new(year: u16, month: u8, day: u8, hour: u8, minute: u8, second: u8) -> PyResult<Self> {
352 let inner = UtcTime::new(year, month, day, hour, minute, second)
353 .map_err(|e| PyValueError::new_err(format!("Invalid UTCTime: {:?}", e)))?;
354 Ok(Self { inner })
355 }
356
357 #[getter]
358 fn year(&self) -> u16 {
359 self.inner.year
360 }
361
362 #[getter]
363 fn month(&self) -> u8 {
364 self.inner.month
365 }
366
367 #[getter]
368 fn day(&self) -> u8 {
369 self.inner.day
370 }
371
372 #[getter]
373 fn hour(&self) -> u8 {
374 self.inner.hour
375 }
376
377 #[getter]
378 fn minute(&self) -> u8 {
379 self.inner.minute
380 }
381
382 #[getter]
383 fn second(&self) -> u8 {
384 self.inner.second
385 }
386
387 fn __str__(&self) -> String {
388 self.inner.to_string()
389 }
390
391 fn __repr__(&self) -> String {
392 format!("UtcTime('{}')", self.inner)
393 }
394}
395
396impl std::fmt::Display for PyUtcTime {
397 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
398 write!(f, "{}", self.inner)
399 }
400}
401
402#[pyclass(name = "GeneralizedTime")]
404#[derive(Debug, Clone)]
405pub struct PyGeneralizedTime {
406 pub(crate) inner: GeneralizedTime,
407}
408
409#[pymethods]
410impl PyGeneralizedTime {
411 #[new]
416 fn new(
417 year: u16,
418 month: u8,
419 day: u8,
420 hour: u8,
421 minute: u8,
422 second: u8,
423 milliseconds: Option<u16>,
424 ) -> PyResult<Self> {
425 let inner = GeneralizedTime::new(year, month, day, hour, minute, second, milliseconds)
426 .map_err(|e| PyValueError::new_err(format!("Invalid GeneralizedTime: {:?}", e)))?;
427 Ok(Self { inner })
428 }
429
430 #[getter]
431 fn year(&self) -> u16 {
432 self.inner.year
433 }
434
435 #[getter]
436 fn month(&self) -> u8 {
437 self.inner.month
438 }
439
440 #[getter]
441 fn day(&self) -> u8 {
442 self.inner.day
443 }
444
445 #[getter]
446 fn hour(&self) -> u8 {
447 self.inner.hour
448 }
449
450 #[getter]
451 fn minute(&self) -> u8 {
452 self.inner.minute
453 }
454
455 #[getter]
456 fn second(&self) -> u8 {
457 self.inner.second
458 }
459
460 #[getter]
461 fn milliseconds(&self) -> Option<u16> {
462 self.inner.milliseconds
463 }
464
465 fn __str__(&self) -> String {
466 self.inner.to_string()
467 }
468
469 fn __repr__(&self) -> String {
470 format!("GeneralizedTime('{}')", self.inner)
471 }
472}
473
474impl std::fmt::Display for PyGeneralizedTime {
475 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
476 write!(f, "{}", self.inner)
477 }
478}
479
480#[pyclass(name = "Real")]
482#[derive(Debug, Clone, Copy)]
483pub struct PyReal {
484 pub(crate) inner: Real,
485}
486
487#[pymethods]
488impl PyReal {
489 #[new]
491 fn new(value: f64) -> Self {
492 Self {
493 inner: Real::new(value),
494 }
495 }
496
497 fn value(&self) -> f64 {
499 self.inner.value()
500 }
501
502 fn is_infinite(&self) -> bool {
504 self.inner.value().is_infinite()
505 }
506
507 fn is_nan(&self) -> bool {
509 self.inner.value().is_nan()
510 }
511
512 fn is_finite(&self) -> bool {
514 self.inner.value().is_finite()
515 }
516
517 fn __float__(&self) -> f64 {
518 self.inner.value()
519 }
520
521 fn __repr__(&self) -> String {
522 format!("Real({})", self.inner.value())
523 }
524
525 fn __str__(&self) -> String {
526 self.inner.value().to_string()
527 }
528
529 fn __eq__(&self, other: &Self) -> bool {
530 self.inner.value() == other.inner.value()
532 }
533
534 fn __hash__(&self, py: Python<'_>) -> PyResult<isize> {
535 self.inner.value().into_pyobject(py)?.hash()
538 }
539}
540
541#[pyclass(name = "Null")]
543#[derive(Debug, Clone, Copy)]
544pub struct PyNull;
545
546#[pymethods]
547impl PyNull {
548 #[new]
549 fn new() -> Self {
550 Self
551 }
552
553 fn __repr__(&self) -> String {
554 "Null()".to_string()
555 }
556
557 fn __eq__(&self, _other: &Self) -> bool {
558 true
559 }
560
561 fn __hash__(&self) -> isize {
562 0
564 }
565}
566
567#[pyclass(name = "Utf8String")]
569#[derive(Debug, Clone)]
570pub struct PyUtf8String {
571 pub(crate) inner: Utf8String,
572}
573
574#[pymethods]
575impl PyUtf8String {
576 #[new]
578 fn new(value: &str) -> Self {
579 Self {
580 inner: Utf8String::new(value.to_string()),
581 }
582 }
583
584 fn as_str(&self) -> &str {
586 self.inner.as_str()
587 }
588
589 fn __str__(&self) -> &str {
590 self.inner.as_str()
591 }
592
593 fn __repr__(&self) -> String {
594 format!("Utf8String('{}')", self.inner.as_str())
595 }
596
597 fn __eq__(&self, other: &Self) -> bool {
598 self.inner == other.inner
599 }
600
601 fn __len__(&self) -> usize {
602 self.inner.as_str().chars().count()
603 }
604}
605
606#[pyclass(name = "PrintableString")]
611#[derive(Debug, Clone)]
612pub struct PyPrintableString {
613 pub(crate) inner: PrintableString,
614}
615
616#[pymethods]
617impl PyPrintableString {
618 #[new]
622 fn new(value: &str) -> PyResult<Self> {
623 let inner = PrintableString::new(value.to_string())
624 .map_err(|e| PyValueError::new_err(format!("Invalid PrintableString: {:?}", e)))?;
625 Ok(Self { inner })
626 }
627
628 fn as_str(&self) -> &str {
630 self.inner.as_str()
631 }
632
633 fn __str__(&self) -> &str {
634 self.inner.as_str()
635 }
636
637 fn __repr__(&self) -> String {
638 format!("PrintableString('{}')", self.inner.as_str())
639 }
640
641 fn __eq__(&self, other: &Self) -> bool {
642 self.inner == other.inner
643 }
644
645 fn __len__(&self) -> usize {
646 self.inner.as_str().chars().count()
647 }
648}
649
650#[pyclass(name = "IA5String")]
652#[derive(Debug, Clone)]
653pub struct PyIA5String {
654 pub(crate) inner: IA5String,
655}
656
657#[pymethods]
658impl PyIA5String {
659 #[new]
663 fn new(value: &str) -> PyResult<Self> {
664 let inner = IA5String::new(value.to_string())
665 .map_err(|e| PyValueError::new_err(format!("Invalid IA5String: {:?}", e)))?;
666 Ok(Self { inner })
667 }
668
669 fn as_str(&self) -> &str {
671 self.inner.as_str()
672 }
673
674 fn __str__(&self) -> &str {
675 self.inner.as_str()
676 }
677
678 fn __repr__(&self) -> String {
679 format!("IA5String('{}')", self.inner.as_str())
680 }
681
682 fn __eq__(&self, other: &Self) -> bool {
683 self.inner == other.inner
684 }
685
686 fn __len__(&self) -> usize {
687 self.inner.as_str().chars().count()
688 }
689}
690
691#[pyclass(name = "NumericString")]
693#[derive(Debug, Clone)]
694pub struct PyNumericString {
695 pub(crate) inner: NumericString,
696}
697
698#[pymethods]
699impl PyNumericString {
700 #[new]
701 fn new(value: &str) -> PyResult<Self> {
702 let inner = NumericString::new(value.to_string())
703 .map_err(|e| PyValueError::new_err(format!("Invalid NumericString: {:?}", e)))?;
704 Ok(Self { inner })
705 }
706
707 fn as_str(&self) -> &str {
708 self.inner.as_str()
709 }
710
711 fn __str__(&self) -> &str {
712 self.inner.as_str()
713 }
714
715 fn __repr__(&self) -> String {
716 format!("NumericString('{}')", self.inner.as_str())
717 }
718
719 fn __eq__(&self, other: &Self) -> bool {
720 self.inner == other.inner
721 }
722
723 fn __len__(&self) -> usize {
724 self.inner.as_str().chars().count()
725 }
726}
727
728#[pyclass(name = "TeletexString")]
730#[derive(Debug, Clone)]
731pub struct PyTeletexString {
732 pub(crate) inner: TeletexString,
733}
734
735#[pymethods]
736impl PyTeletexString {
737 #[new]
739 fn new(data: Vec<u8>) -> Self {
740 Self {
741 inner: TeletexString::new(data),
742 }
743 }
744
745 #[staticmethod]
749 fn from_latin1(value: &str) -> PyResult<Self> {
750 TeletexString::from_latin1(value)
751 .map(|inner| Self { inner })
752 .map_err(|e| PyValueError::new_err(format!("{:?}", e)))
753 }
754
755 #[staticmethod]
759 fn from_str(value: &str) -> PyResult<Self> {
760 TeletexString::from_latin1(value)
761 .map(|inner| Self { inner })
762 .map_err(|e| PyValueError::new_err(format!("{:?}", e)))
763 }
764
765 fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
767 PyBytes::new(py, self.inner.as_bytes())
768 }
769
770 fn as_str(&self) -> String {
772 self.inner.as_latin1_string()
773 }
774
775 fn __str__(&self) -> String {
776 self.inner.as_latin1_string()
777 }
778
779 fn __repr__(&self) -> String {
780 format!("TeletexString(<{} bytes>)", self.inner.as_bytes().len())
781 }
782
783 fn __len__(&self) -> usize {
784 self.inner.as_bytes().len()
785 }
786
787 fn __eq__(&self, other: &Self) -> bool {
788 self.inner == other.inner
789 }
790}
791
792#[pyclass(name = "VisibleString")]
794#[derive(Debug, Clone)]
795pub struct PyVisibleString {
796 pub(crate) inner: VisibleString,
797}
798
799#[pymethods]
800impl PyVisibleString {
801 #[new]
802 fn new(value: &str) -> PyResult<Self> {
803 let inner = VisibleString::new(value.to_string())
804 .map_err(|e| PyValueError::new_err(format!("Invalid VisibleString: {:?}", e)))?;
805 Ok(Self { inner })
806 }
807
808 fn as_str(&self) -> &str {
809 self.inner.as_str()
810 }
811
812 fn __str__(&self) -> &str {
813 self.inner.as_str()
814 }
815
816 fn __repr__(&self) -> String {
817 format!("VisibleString('{}')", self.inner.as_str())
818 }
819
820 fn __eq__(&self, other: &Self) -> bool {
821 self.inner == other.inner
822 }
823
824 fn __len__(&self) -> usize {
825 self.inner.as_str().len()
826 }
827}
828
829#[pyclass(name = "GeneralString")]
831#[derive(Debug, Clone)]
832pub struct PyGeneralString {
833 pub(crate) inner: GeneralString,
834}
835
836#[pymethods]
837impl PyGeneralString {
838 #[new]
840 fn new(data: Vec<u8>) -> Self {
841 Self {
842 inner: GeneralString::new(data),
843 }
844 }
845
846 #[staticmethod]
850 fn from_ascii(value: &str) -> PyResult<Self> {
851 GeneralString::from_ascii(value)
852 .map(|inner| Self { inner })
853 .map_err(|e| PyValueError::new_err(format!("{:?}", e)))
854 }
855
856 #[staticmethod]
860 fn from_str(value: &str) -> PyResult<Self> {
861 GeneralString::from_ascii(value)
862 .map(|inner| Self { inner })
863 .map_err(|e| PyValueError::new_err(format!("{:?}", e)))
864 }
865
866 fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
868 PyBytes::new(py, self.inner.as_bytes())
869 }
870
871 fn as_str(&self) -> String {
873 self.inner.as_latin1_string()
874 }
875
876 fn __str__(&self) -> String {
877 self.inner.as_latin1_string()
878 }
879
880 fn __repr__(&self) -> String {
881 format!("GeneralString(<{} bytes>)", self.inner.as_bytes().len())
882 }
883
884 fn __len__(&self) -> usize {
885 self.inner.as_bytes().len()
886 }
887
888 fn __eq__(&self, other: &Self) -> bool {
889 self.inner == other.inner
890 }
891}
892
893#[pyclass(name = "UniversalString")]
895#[derive(Debug, Clone)]
896pub struct PyUniversalString {
897 pub(crate) inner: UniversalString,
898}
899
900#[pymethods]
901impl PyUniversalString {
902 #[new]
904 fn new(value: &str) -> Self {
905 Self {
906 inner: UniversalString::new(value.to_string()),
907 }
908 }
909
910 #[staticmethod]
912 fn from_bytes(data: &[u8]) -> PyResult<Self> {
913 let inner = UniversalString::from_ucs4_be(data)
914 .map_err(|e| PyValueError::new_err(format!("Invalid UniversalString: {:?}", e)))?;
915 Ok(Self { inner })
916 }
917
918 fn as_str(&self) -> &str {
919 self.inner.as_str()
920 }
921
922 fn __str__(&self) -> &str {
923 self.inner.as_str()
924 }
925
926 fn __repr__(&self) -> String {
927 format!("UniversalString('{}')", self.inner.as_str())
928 }
929
930 fn __eq__(&self, other: &Self) -> bool {
931 self.inner == other.inner
932 }
933
934 fn __len__(&self) -> usize {
935 self.inner.as_str().chars().count()
936 }
937}
938
939#[pyclass(name = "BmpString")]
941#[derive(Debug, Clone)]
942pub struct PyBmpString {
943 pub(crate) inner: BmpString,
944}
945
946#[pymethods]
947impl PyBmpString {
948 #[new]
950 fn new(value: &str) -> PyResult<Self> {
951 let inner = BmpString::new(value.to_string())
952 .map_err(|e| PyValueError::new_err(format!("Invalid BMPString: {:?}", e)))?;
953 Ok(Self { inner })
954 }
955
956 #[staticmethod]
958 fn from_bytes(data: &[u8]) -> PyResult<Self> {
959 let inner = BmpString::from_ucs2_be(data)
960 .map_err(|e| PyValueError::new_err(format!("Invalid BMPString: {:?}", e)))?;
961 Ok(Self { inner })
962 }
963
964 fn as_str(&self) -> &str {
965 self.inner.as_str()
966 }
967
968 fn __str__(&self) -> &str {
969 self.inner.as_str()
970 }
971
972 fn __repr__(&self) -> String {
973 format!("BmpString('{}')", self.inner.as_str())
974 }
975
976 fn __eq__(&self, other: &Self) -> bool {
977 self.inner == other.inner
978 }
979
980 fn __len__(&self) -> usize {
981 self.inner.as_str().chars().count()
982 }
983}
984
985#[pyclass(name = "TaggedElement")]
991#[derive(Debug)]
992pub struct PyTaggedElement {
993 pub(crate) tag_number: u32,
994 pub(crate) tag_class: String,
995 pub(crate) is_constructed: bool,
996 pub(crate) value: Py<PyAny>,
997}
998
999#[pymethods]
1000impl PyTaggedElement {
1001 #[getter]
1003 fn tag_number(&self) -> u32 {
1004 self.tag_number
1005 }
1006
1007 #[getter]
1009 fn tag_class(&self) -> &str {
1010 &self.tag_class
1011 }
1012
1013 #[getter]
1015 fn is_constructed(&self) -> bool {
1016 self.is_constructed
1017 }
1018
1019 #[getter]
1021 fn value(&self, py: Python<'_>) -> Py<PyAny> {
1022 self.value.clone_ref(py)
1023 }
1024
1025 fn __repr__(&self) -> String {
1026 format!(
1027 "TaggedElement(tag={}, class='{}', constructed={})",
1028 self.tag_number, self.tag_class, self.is_constructed
1029 )
1030 }
1031}
1032
1033#[pyclass(name = "RawElement")]
1038#[derive(Debug, Clone)]
1039pub struct PyRawElement {
1040 pub(crate) tag_number: u32,
1042 pub(crate) tag_class: String,
1044 pub(crate) is_constructed: bool,
1046 pub(crate) data: Vec<u8>,
1048}
1049
1050#[pymethods]
1051impl PyRawElement {
1052 #[getter]
1053 fn tag_number(&self) -> u32 {
1054 self.tag_number
1055 }
1056
1057 #[getter]
1058 fn tag_class(&self) -> &str {
1059 &self.tag_class
1060 }
1061
1062 #[getter]
1063 fn is_constructed(&self) -> bool {
1064 self.is_constructed
1065 }
1066
1067 #[getter]
1068 fn data<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
1069 PyBytes::new(py, &self.data)
1070 }
1071
1072 fn __repr__(&self) -> String {
1073 format!(
1074 "RawElement(tag={}, class='{}', constructed={}, {} bytes)",
1075 self.tag_number,
1076 self.tag_class,
1077 self.is_constructed,
1078 self.data.len()
1079 )
1080 }
1081}
1082
1083fn tag_class_str(class: synta::TagClass) -> &'static str {
1089 match class {
1090 synta::TagClass::Universal => "Universal",
1091 synta::TagClass::Application => "Application",
1092 synta::TagClass::ContextSpecific => "Context",
1093 synta::TagClass::Private => "Private",
1094 }
1095}
1096
1097pub(crate) fn element_to_pyobject(element: synta::Element<'_>, py: Python) -> PyResult<Py<PyAny>> {
1102 use synta::Element;
1103 match element {
1104 Element::Boolean(v) => Py::new(py, PyBoolean { inner: v }).map(|p| p.into_any()),
1105 Element::Integer(v) => Py::new(py, PyInteger { inner: v }).map(|p| p.into_any()),
1106 Element::BitString(v) => Py::new(
1107 py,
1108 PyBitString {
1109 inner: v.to_owned(),
1110 },
1111 )
1112 .map(|p| p.into_any()),
1113 Element::OctetString(v) => Py::new(
1114 py,
1115 PyOctetString {
1116 inner: v.to_owned(),
1117 },
1118 )
1119 .map(|p| p.into_any()),
1120 Element::Null(_) => Py::new(py, PyNull).map(|p| p.into_any()),
1121 Element::Real(v) => Py::new(py, PyReal { inner: v }).map(|p| p.into_any()),
1122 Element::ObjectIdentifier(v) => {
1123 Py::new(py, PyObjectIdentifier::from_oid(v)).map(|p| p.into_any())
1124 }
1125 Element::Utf8String(v) => Py::new(
1126 py,
1127 PyUtf8String {
1128 inner: v.to_owned(),
1129 },
1130 )
1131 .map(|p| p.into_any()),
1132 Element::PrintableString(v) => Py::new(
1133 py,
1134 PyPrintableString {
1135 inner: v.to_owned(),
1136 },
1137 )
1138 .map(|p| p.into_any()),
1139 Element::IA5String(v) => Py::new(
1140 py,
1141 PyIA5String {
1142 inner: v.to_owned(),
1143 },
1144 )
1145 .map(|p| p.into_any()),
1146 Element::UtcTime(v) => Py::new(py, PyUtcTime { inner: v }).map(|p| p.into_any()),
1147 Element::GeneralizedTime(v) => {
1148 Py::new(py, PyGeneralizedTime { inner: v }).map(|p| p.into_any())
1149 }
1150 Element::Sequence(seq) => {
1151 let list = pyo3::types::PyList::empty(py);
1152 for elem in seq.into_elements().map_err(|e| {
1153 pyo3::exceptions::PyValueError::new_err(format!("ASN.1 decode error: {}", e))
1154 })? {
1155 list.append(element_to_pyobject(elem, py)?)?;
1156 }
1157 Ok(list.into_any().unbind())
1158 }
1159 Element::Set(set) => {
1160 let list = pyo3::types::PyList::empty(py);
1161 for elem in set.into_elements().map_err(|e| {
1162 pyo3::exceptions::PyValueError::new_err(format!("ASN.1 decode error: {}", e))
1163 })? {
1164 list.append(element_to_pyobject(elem, py)?)?;
1165 }
1166 Ok(list.into_any().unbind())
1167 }
1168 Element::Tagged(tag, inner) => {
1169 let value = element_to_pyobject(*inner, py)?;
1170 Py::new(
1171 py,
1172 PyTaggedElement {
1173 tag_number: tag.number(),
1174 tag_class: tag_class_str(tag.class()).to_string(),
1175 is_constructed: tag.is_constructed(),
1176 value,
1177 },
1178 )
1179 .map(|p| p.into_any())
1180 }
1181 Element::NumericString(v) => {
1182 Py::new(py, PyNumericString { inner: v }).map(|p| p.into_any())
1183 }
1184 Element::TeletexString(v) => {
1185 Py::new(py, PyTeletexString { inner: v }).map(|p| p.into_any())
1186 }
1187 Element::VisibleString(v) => {
1188 Py::new(py, PyVisibleString { inner: v }).map(|p| p.into_any())
1189 }
1190 Element::GeneralString(v) => {
1191 Py::new(py, PyGeneralString { inner: v }).map(|p| p.into_any())
1192 }
1193 Element::UniversalString(v) => {
1194 Py::new(py, PyUniversalString { inner: v }).map(|p| p.into_any())
1195 }
1196 Element::BmpString(v) => Py::new(py, PyBmpString { inner: v }).map(|p| p.into_any()),
1197 Element::Raw(tag, bytes) => {
1198 let raw = PyRawElement {
1199 tag_number: tag.number(),
1200 tag_class: tag_class_str(tag.class()).to_string(),
1201 is_constructed: tag.is_constructed(),
1202 data: bytes.to_vec(),
1203 };
1204 Py::new(py, raw).map(|p| p.into_any())
1205 }
1206 }
1207}