1use pyo3::prelude::*;
5use pyo3::types::PyBytes;
6
7use super::primitives::{
8 PyBitString, PyBoolean, PyGeneralizedTime, PyInteger, PyNull, PyOctetString, PyReal, PyUtcTime,
9};
10use super::strings::{
11 PyBmpString, PyGeneralString, PyIA5String, PyNumericString, PyPrintableString, PyTeletexString,
12 PyUniversalString, PyUtf8String, PyVisibleString,
13};
14use super::PyObjectIdentifier;
15
16#[pyclass(name = "TaggedElement")]
22#[derive(Debug)]
23pub struct PyTaggedElement {
24 pub(crate) tag_number: u32,
25 pub(crate) tag_class: String,
26 pub(crate) is_constructed: bool,
27 pub(crate) value: Py<PyAny>,
28}
29
30#[pymethods]
31impl PyTaggedElement {
32 #[getter]
34 fn tag_number(&self) -> u32 {
35 self.tag_number
36 }
37
38 #[getter]
40 fn tag_class(&self) -> &str {
41 &self.tag_class
42 }
43
44 #[getter]
46 fn is_constructed(&self) -> bool {
47 self.is_constructed
48 }
49
50 #[getter]
52 fn value(&self, py: Python<'_>) -> Py<PyAny> {
53 self.value.clone_ref(py)
54 }
55
56 fn __repr__(&self) -> String {
57 format!(
58 "TaggedElement(tag={}, class='{}', constructed={})",
59 self.tag_number, self.tag_class, self.is_constructed
60 )
61 }
62}
63
64#[pyclass(name = "RawElement")]
69#[derive(Debug, Clone)]
70pub struct PyRawElement {
71 pub(crate) tag_number: u32,
73 pub(crate) tag_class: String,
75 pub(crate) is_constructed: bool,
77 pub(crate) data: Vec<u8>,
79}
80
81#[pymethods]
82impl PyRawElement {
83 #[getter]
84 fn tag_number(&self) -> u32 {
85 self.tag_number
86 }
87
88 #[getter]
89 fn tag_class(&self) -> &str {
90 &self.tag_class
91 }
92
93 #[getter]
94 fn is_constructed(&self) -> bool {
95 self.is_constructed
96 }
97
98 #[getter]
99 fn data<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
100 PyBytes::new(py, &self.data)
101 }
102
103 fn __repr__(&self) -> String {
104 format!(
105 "RawElement(tag={}, class='{}', constructed={}, {} bytes)",
106 self.tag_number,
107 self.tag_class,
108 self.is_constructed,
109 self.data.len()
110 )
111 }
112}
113
114pub(crate) fn tag_class_str(class: synta::TagClass) -> &'static str {
120 match class {
121 synta::TagClass::Universal => "Universal",
122 synta::TagClass::Application => "Application",
123 synta::TagClass::ContextSpecific => "Context",
124 synta::TagClass::Private => "Private",
125 }
126}
127
128pub fn element_to_pyobject(element: synta::Element<'_>, py: Python) -> PyResult<Py<PyAny>> {
133 use synta::Element;
134 match element {
135 Element::Boolean(v) => Py::new(py, PyBoolean { inner: v }).map(|p| p.into_any()),
136 Element::Integer(v) => Py::new(py, PyInteger { inner: v }).map(|p| p.into_any()),
137 Element::BitString(v) => Py::new(
138 py,
139 PyBitString {
140 inner: v.to_owned(),
141 },
142 )
143 .map(|p| p.into_any()),
144 Element::OctetString(v) => Py::new(
145 py,
146 PyOctetString {
147 inner: v.to_owned(),
148 },
149 )
150 .map(|p| p.into_any()),
151 Element::Null(_) => Py::new(py, PyNull).map(|p| p.into_any()),
152 Element::Real(v) => Py::new(py, PyReal { inner: v }).map(|p| p.into_any()),
153 Element::ObjectIdentifier(v) => {
154 Py::new(py, PyObjectIdentifier::from_oid(v)).map(|p| p.into_any())
155 }
156 Element::Utf8String(v) => Py::new(
157 py,
158 PyUtf8String {
159 inner: v.to_owned(),
160 },
161 )
162 .map(|p| p.into_any()),
163 Element::PrintableString(v) => Py::new(
164 py,
165 PyPrintableString {
166 inner: v.to_owned(),
167 },
168 )
169 .map(|p| p.into_any()),
170 Element::IA5String(v) => Py::new(
171 py,
172 PyIA5String {
173 inner: v.to_owned(),
174 },
175 )
176 .map(|p| p.into_any()),
177 Element::UtcTime(v) => Py::new(py, PyUtcTime { inner: v }).map(|p| p.into_any()),
178 Element::GeneralizedTime(v) => {
179 Py::new(py, PyGeneralizedTime { inner: v }).map(|p| p.into_any())
180 }
181 Element::Sequence(seq) => {
182 let list = pyo3::types::PyList::empty(py);
183 for elem in seq.into_elements().map_err(|e| {
184 pyo3::exceptions::PyValueError::new_err(format!("ASN.1 decode error: {}", e))
185 })? {
186 list.append(element_to_pyobject(elem, py)?)?;
187 }
188 Ok(list.into_any().unbind())
189 }
190 Element::Set(set) => {
191 let list = pyo3::types::PyList::empty(py);
192 for elem in set.into_elements().map_err(|e| {
193 pyo3::exceptions::PyValueError::new_err(format!("ASN.1 decode error: {}", e))
194 })? {
195 list.append(element_to_pyobject(elem, py)?)?;
196 }
197 Ok(list.into_any().unbind())
198 }
199 Element::Tagged(tag, inner) => {
200 let value = element_to_pyobject(*inner, py)?;
201 Py::new(
202 py,
203 PyTaggedElement {
204 tag_number: tag.number(),
205 tag_class: tag_class_str(tag.class()).to_string(),
206 is_constructed: tag.is_constructed(),
207 value,
208 },
209 )
210 .map(|p| p.into_any())
211 }
212 Element::NumericString(v) => {
213 Py::new(py, PyNumericString { inner: v }).map(|p| p.into_any())
214 }
215 Element::TeletexString(v) => {
216 Py::new(py, PyTeletexString { inner: v }).map(|p| p.into_any())
217 }
218 Element::VisibleString(v) => {
219 Py::new(py, PyVisibleString { inner: v }).map(|p| p.into_any())
220 }
221 Element::GeneralString(v) => {
222 Py::new(py, PyGeneralString { inner: v }).map(|p| p.into_any())
223 }
224 Element::UniversalString(v) => {
225 Py::new(py, PyUniversalString { inner: v }).map(|p| p.into_any())
226 }
227 Element::BmpString(v) => Py::new(py, PyBmpString { inner: v }).map(|p| p.into_any()),
228 Element::Raw(tag, bytes) => {
229 let raw = PyRawElement {
230 tag_number: tag.number(),
231 tag_class: tag_class_str(tag.class()).to_string(),
232 is_constructed: tag.is_constructed(),
233 data: bytes.to_vec(),
234 };
235 Py::new(py, raw).map(|p| p.into_any())
236 }
237 }
238}