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]
85 fn tag_number(&self) -> u32 {
86 self.tag_number
87 }
88
89 #[getter]
91 fn tag_class(&self) -> &str {
92 &self.tag_class
93 }
94
95 #[getter]
97 fn is_constructed(&self) -> bool {
98 self.is_constructed
99 }
100
101 #[getter]
103 fn data<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
104 PyBytes::new(py, &self.data)
105 }
106
107 fn __repr__(&self) -> String {
108 format!(
109 "RawElement(tag={}, class='{}', constructed={}, {} bytes)",
110 self.tag_number,
111 self.tag_class,
112 self.is_constructed,
113 self.data.len()
114 )
115 }
116}
117
118pub(crate) fn tag_class_str(class: synta::TagClass) -> &'static str {
124 match class {
125 synta::TagClass::Universal => "Universal",
126 synta::TagClass::Application => "Application",
127 synta::TagClass::ContextSpecific => "Context",
128 synta::TagClass::Private => "Private",
129 }
130}
131
132pub fn element_to_pyobject(element: synta::Element<'_>, py: Python) -> PyResult<Py<PyAny>> {
137 use synta::Element;
138 match element {
139 Element::Boolean(v) => Py::new(py, PyBoolean { inner: v }).map(|p| p.into_any()),
140 Element::Integer(v) => Py::new(py, PyInteger { inner: v }).map(|p| p.into_any()),
141 Element::BitString(v) => Py::new(
142 py,
143 PyBitString {
144 inner: v.to_owned(),
145 },
146 )
147 .map(|p| p.into_any()),
148 Element::OctetString(v) => Py::new(
149 py,
150 PyOctetString {
151 inner: v.to_owned(),
152 },
153 )
154 .map(|p| p.into_any()),
155 Element::Null(_) => Py::new(py, PyNull).map(|p| p.into_any()),
156 Element::Real(v) => Py::new(py, PyReal { inner: v }).map(|p| p.into_any()),
157 Element::ObjectIdentifier(v) => {
158 Py::new(py, PyObjectIdentifier::from_oid(v)).map(|p| p.into_any())
159 }
160 Element::Utf8String(v) => Py::new(
161 py,
162 PyUtf8String {
163 inner: v.to_owned(),
164 },
165 )
166 .map(|p| p.into_any()),
167 Element::PrintableString(v) => Py::new(
168 py,
169 PyPrintableString {
170 inner: v.to_owned(),
171 },
172 )
173 .map(|p| p.into_any()),
174 Element::IA5String(v) => Py::new(
175 py,
176 PyIA5String {
177 inner: v.to_owned(),
178 },
179 )
180 .map(|p| p.into_any()),
181 Element::UtcTime(v) => Py::new(py, PyUtcTime { inner: v }).map(|p| p.into_any()),
182 Element::GeneralizedTime(v) => {
183 Py::new(py, PyGeneralizedTime { inner: v }).map(|p| p.into_any())
184 }
185 Element::Sequence(seq) => {
186 let list = pyo3::types::PyList::empty(py);
187 for elem in seq.into_elements().map_err(|e| {
188 pyo3::exceptions::PyValueError::new_err(format!("ASN.1 decode error: {}", e))
189 })? {
190 list.append(element_to_pyobject(elem, py)?)?;
191 }
192 Ok(list.into_any().unbind())
193 }
194 Element::Set(set) => {
195 let list = pyo3::types::PyList::empty(py);
196 for elem in set.into_elements().map_err(|e| {
197 pyo3::exceptions::PyValueError::new_err(format!("ASN.1 decode error: {}", e))
198 })? {
199 list.append(element_to_pyobject(elem, py)?)?;
200 }
201 Ok(list.into_any().unbind())
202 }
203 Element::Tagged(tag, inner) => {
204 let value = element_to_pyobject(*inner, py)?;
205 Py::new(
206 py,
207 PyTaggedElement {
208 tag_number: tag.number(),
209 tag_class: tag_class_str(tag.class()).to_string(),
210 is_constructed: tag.is_constructed(),
211 value,
212 },
213 )
214 .map(|p| p.into_any())
215 }
216 Element::NumericString(v) => {
217 Py::new(py, PyNumericString { inner: v }).map(|p| p.into_any())
218 }
219 Element::TeletexString(v) => {
220 Py::new(py, PyTeletexString { inner: v }).map(|p| p.into_any())
221 }
222 Element::VisibleString(v) => {
223 Py::new(py, PyVisibleString { inner: v }).map(|p| p.into_any())
224 }
225 Element::GeneralString(v) => {
226 Py::new(py, PyGeneralString { inner: v }).map(|p| p.into_any())
227 }
228 Element::UniversalString(v) => {
229 Py::new(py, PyUniversalString { inner: v }).map(|p| p.into_any())
230 }
231 Element::BmpString(v) => Py::new(py, PyBmpString { inner: v }).map(|p| p.into_any()),
232 Element::Raw(tag, bytes) => {
233 let raw = PyRawElement {
234 tag_number: tag.number(),
235 tag_class: tag_class_str(tag.class()).to_string(),
236 is_constructed: tag.is_constructed(),
237 data: bytes.to_vec(),
238 };
239 Py::new(py, raw).map(|p| p.into_any())
240 }
241 }
242}