use pyo3::prelude::*;
use pyo3::types::PyBytes;
use super::primitives::{
PyBitString, PyBoolean, PyGeneralizedTime, PyInteger, PyNull, PyOctetString, PyReal, PyUtcTime,
};
use super::strings::{
PyBmpString, PyGeneralString, PyIA5String, PyNumericString, PyPrintableString, PyTeletexString,
PyUniversalString, PyUtf8String, PyVisibleString,
};
use super::PyObjectIdentifier;
#[pyclass(name = "TaggedElement")]
#[derive(Debug)]
pub struct PyTaggedElement {
pub(crate) tag_number: u32,
pub(crate) tag_class: String,
pub(crate) is_constructed: bool,
pub(crate) value: Py<PyAny>,
}
#[pymethods]
impl PyTaggedElement {
#[getter]
fn tag_number(&self) -> u32 {
self.tag_number
}
#[getter]
fn tag_class(&self) -> &str {
&self.tag_class
}
#[getter]
fn is_constructed(&self) -> bool {
self.is_constructed
}
#[getter]
fn value(&self, py: Python<'_>) -> Py<PyAny> {
self.value.clone_ref(py)
}
fn __repr__(&self) -> String {
format!(
"TaggedElement(tag={}, class='{}', constructed={})",
self.tag_number, self.tag_class, self.is_constructed
)
}
}
#[pyclass(name = "RawElement")]
#[derive(Debug, Clone)]
pub struct PyRawElement {
pub(crate) tag_number: u32,
pub(crate) tag_class: String,
pub(crate) is_constructed: bool,
pub(crate) data: Vec<u8>,
}
#[pymethods]
impl PyRawElement {
#[getter]
fn tag_number(&self) -> u32 {
self.tag_number
}
#[getter]
fn tag_class(&self) -> &str {
&self.tag_class
}
#[getter]
fn is_constructed(&self) -> bool {
self.is_constructed
}
#[getter]
fn data<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.data)
}
fn __repr__(&self) -> String {
format!(
"RawElement(tag={}, class='{}', constructed={}, {} bytes)",
self.tag_number,
self.tag_class,
self.is_constructed,
self.data.len()
)
}
}
pub(crate) fn tag_class_str(class: synta::TagClass) -> &'static str {
match class {
synta::TagClass::Universal => "Universal",
synta::TagClass::Application => "Application",
synta::TagClass::ContextSpecific => "Context",
synta::TagClass::Private => "Private",
}
}
pub fn element_to_pyobject(element: synta::Element<'_>, py: Python) -> PyResult<Py<PyAny>> {
use synta::Element;
match element {
Element::Boolean(v) => Py::new(py, PyBoolean { inner: v }).map(|p| p.into_any()),
Element::Integer(v) => Py::new(py, PyInteger { inner: v }).map(|p| p.into_any()),
Element::BitString(v) => Py::new(
py,
PyBitString {
inner: v.to_owned(),
},
)
.map(|p| p.into_any()),
Element::OctetString(v) => Py::new(
py,
PyOctetString {
inner: v.to_owned(),
},
)
.map(|p| p.into_any()),
Element::Null(_) => Py::new(py, PyNull).map(|p| p.into_any()),
Element::Real(v) => Py::new(py, PyReal { inner: v }).map(|p| p.into_any()),
Element::ObjectIdentifier(v) => {
Py::new(py, PyObjectIdentifier::from_oid(v)).map(|p| p.into_any())
}
Element::Utf8String(v) => Py::new(
py,
PyUtf8String {
inner: v.to_owned(),
},
)
.map(|p| p.into_any()),
Element::PrintableString(v) => Py::new(
py,
PyPrintableString {
inner: v.to_owned(),
},
)
.map(|p| p.into_any()),
Element::IA5String(v) => Py::new(
py,
PyIA5String {
inner: v.to_owned(),
},
)
.map(|p| p.into_any()),
Element::UtcTime(v) => Py::new(py, PyUtcTime { inner: v }).map(|p| p.into_any()),
Element::GeneralizedTime(v) => {
Py::new(py, PyGeneralizedTime { inner: v }).map(|p| p.into_any())
}
Element::Sequence(seq) => {
let list = pyo3::types::PyList::empty(py);
for elem in seq.into_elements().map_err(|e| {
pyo3::exceptions::PyValueError::new_err(format!("ASN.1 decode error: {}", e))
})? {
list.append(element_to_pyobject(elem, py)?)?;
}
Ok(list.into_any().unbind())
}
Element::Set(set) => {
let list = pyo3::types::PyList::empty(py);
for elem in set.into_elements().map_err(|e| {
pyo3::exceptions::PyValueError::new_err(format!("ASN.1 decode error: {}", e))
})? {
list.append(element_to_pyobject(elem, py)?)?;
}
Ok(list.into_any().unbind())
}
Element::Tagged(tag, inner) => {
let value = element_to_pyobject(*inner, py)?;
Py::new(
py,
PyTaggedElement {
tag_number: tag.number(),
tag_class: tag_class_str(tag.class()).to_string(),
is_constructed: tag.is_constructed(),
value,
},
)
.map(|p| p.into_any())
}
Element::NumericString(v) => {
Py::new(py, PyNumericString { inner: v }).map(|p| p.into_any())
}
Element::TeletexString(v) => {
Py::new(py, PyTeletexString { inner: v }).map(|p| p.into_any())
}
Element::VisibleString(v) => {
Py::new(py, PyVisibleString { inner: v }).map(|p| p.into_any())
}
Element::GeneralString(v) => {
Py::new(py, PyGeneralString { inner: v }).map(|p| p.into_any())
}
Element::UniversalString(v) => {
Py::new(py, PyUniversalString { inner: v }).map(|p| p.into_any())
}
Element::BmpString(v) => Py::new(py, PyBmpString { inner: v }).map(|p| p.into_any()),
Element::Raw(tag, bytes) => {
let raw = PyRawElement {
tag_number: tag.number(),
tag_class: tag_class_str(tag.class()).to_string(),
is_constructed: tag.is_constructed(),
data: bytes.to_vec(),
};
Py::new(py, raw).map(|p| p.into_any())
}
}
}