use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::str::FromStr;
use std::sync::OnceLock;
use pyo3::exceptions::{PyOverflowError, PyValueError};
use pyo3::prelude::*;
use pyo3::types::{PyBytes, PyString, PyTuple};
use synta::{
BitString, BmpString, Boolean, GeneralString, GeneralizedTime, IA5String, Integer,
NumericString, ObjectIdentifier, OctetString, PrintableString, Real, TeletexString,
UniversalString, UtcTime, Utf8String, VisibleString,
};
#[pyclass(frozen, name = "ObjectIdentifier")]
#[derive(Debug, Clone)]
pub struct PyObjectIdentifier {
pub(crate) inner: ObjectIdentifier,
dotted_cache: OnceLock<String>,
}
impl PyObjectIdentifier {
pub fn from_oid(inner: ObjectIdentifier) -> Self {
Self {
inner,
dotted_cache: OnceLock::new(),
}
}
}
#[pymethods]
impl PyObjectIdentifier {
#[new]
fn new(oid_str: &str) -> PyResult<Self> {
let inner = ObjectIdentifier::from_str(oid_str)
.map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("Invalid OID: {e:?}")))?;
Ok(Self {
inner,
dotted_cache: OnceLock::new(),
})
}
#[staticmethod]
fn from_components(components: Vec<u32>) -> PyResult<Self> {
let inner = ObjectIdentifier::new(&components)
.map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("Invalid OID: {e:?}")))?;
Ok(Self {
inner,
dotted_cache: OnceLock::new(),
})
}
#[staticmethod]
fn from_der_value(data: &[u8]) -> PyResult<Self> {
let inner = ObjectIdentifier::from_content_bytes(data).map_err(|e| {
pyo3::exceptions::PyValueError::new_err(format!("Invalid OID content: {e:?}"))
})?;
Ok(Self {
inner,
dotted_cache: OnceLock::new(),
})
}
fn components<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
PyTuple::new(py, self.inner.components())
}
fn __str__(&self) -> &str {
self.dotted_cache.get_or_init(|| self.inner.to_string())
}
fn __repr__(&self) -> String {
format!(
"ObjectIdentifier('{}')",
self.dotted_cache.get_or_init(|| self.inner.to_string())
)
}
fn __eq__(&self, other: &Bound<'_, PyAny>) -> bool {
if let Ok(other_oid) = other.extract::<PyRef<PyObjectIdentifier>>() {
return self.inner == other_oid.inner;
}
if let Ok(s) = other.extract::<String>() {
return self.dotted_cache.get_or_init(|| self.inner.to_string()) == &s;
}
false
}
fn __hash__(&self, py: Python<'_>) -> PyResult<isize> {
PyString::new(py, self.dotted_cache.get_or_init(|| self.inner.to_string())).hash()
}
}
#[pyclass(name = "Integer")]
#[derive(Debug, Clone)]
pub struct PyInteger {
pub(crate) inner: Integer,
}
#[pymethods]
impl PyInteger {
#[new]
fn new(value: i64) -> Self {
Self {
inner: Integer::from_i64(value),
}
}
fn to_int(&self) -> PyResult<i64> {
self.inner
.as_i64()
.map_err(|_| PyOverflowError::new_err("Integer too large for i64"))
}
fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, self.inner.as_bytes())
}
#[staticmethod]
fn from_bytes(bytes: &[u8]) -> Self {
Self {
inner: Integer::from_bytes(bytes),
}
}
#[staticmethod]
fn from_u64(value: u64) -> Self {
Self {
inner: Integer::from_u64(value),
}
}
fn to_i128(&self) -> PyResult<i128> {
self.inner
.as_i128()
.map_err(|_| PyOverflowError::new_err("Integer too large for i128"))
}
fn __eq__(&self, other: &Self) -> bool {
self.inner.as_bytes() == other.inner.as_bytes()
}
fn __hash__(&self) -> u64 {
let mut h = DefaultHasher::new();
self.inner.as_bytes().hash(&mut h);
h.finish()
}
fn __repr__(&self) -> PyResult<String> {
match self.inner.as_i64() {
Ok(val) => Ok(format!("Integer({})", val)),
Err(_) => Ok(format!("Integer(<{} bytes>)", self.inner.as_bytes().len())),
}
}
fn __str__(&self) -> PyResult<String> {
match self.inner.as_i64() {
Ok(val) => Ok(val.to_string()),
Err(_) => Ok(format!("<integer {} bytes>", self.inner.as_bytes().len())),
}
}
}
#[pyclass(name = "OctetString")]
#[derive(Debug, Clone)]
pub struct PyOctetString {
pub(crate) inner: OctetString,
}
#[pymethods]
impl PyOctetString {
#[new]
fn new(data: Vec<u8>) -> Self {
Self {
inner: OctetString::new(data),
}
}
fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, self.inner.as_bytes())
}
fn __len__(&self) -> usize {
self.inner.as_bytes().len()
}
fn __eq__(&self, other: &Self) -> bool {
self.inner == other.inner
}
fn __repr__(&self) -> String {
format!("OctetString(<{} bytes>)", self.inner.as_bytes().len())
}
}
#[pyclass(name = "BitString")]
#[derive(Debug, Clone)]
pub struct PyBitString {
pub(crate) inner: BitString,
}
#[pymethods]
impl PyBitString {
#[new]
fn new(data: Vec<u8>, unused_bits: u8) -> PyResult<Self> {
if unused_bits > 7 {
return Err(PyValueError::new_err("unused_bits must be 0-7"));
}
let inner = BitString::new(data, unused_bits)
.map_err(|e| PyValueError::new_err(format!("Invalid BitString: {:?}", e)))?;
Ok(Self { inner })
}
fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, self.inner.as_bytes())
}
fn unused_bits(&self) -> u8 {
self.inner.unused_bits()
}
fn bit_len(&self) -> usize {
self.inner.bit_len()
}
fn __len__(&self) -> usize {
self.inner.bit_len()
}
fn __eq__(&self, other: &Self) -> bool {
self.inner == other.inner
}
fn __repr__(&self) -> String {
format!(
"BitString(<{} bits, {} unused>)",
self.inner.bit_len(),
self.inner.unused_bits()
)
}
}
#[pyclass(name = "Boolean")]
#[derive(Debug, Clone, Copy)]
pub struct PyBoolean {
pub(crate) inner: Boolean,
}
#[pymethods]
impl PyBoolean {
#[new]
fn new(value: bool) -> Self {
Self {
inner: Boolean::new(value),
}
}
fn value(&self) -> bool {
self.inner.value()
}
fn __bool__(&self) -> bool {
self.inner.value()
}
fn __eq__(&self, other: &Self) -> bool {
self.inner.value() == other.inner.value()
}
fn __hash__(&self) -> isize {
self.inner.value() as isize
}
fn __repr__(&self) -> String {
format!("Boolean({})", self.inner.value())
}
}
#[pyclass(name = "UtcTime")]
#[derive(Debug, Clone)]
pub struct PyUtcTime {
pub(crate) inner: UtcTime,
}
#[pymethods]
impl PyUtcTime {
#[new]
fn new(year: u16, month: u8, day: u8, hour: u8, minute: u8, second: u8) -> PyResult<Self> {
let inner = UtcTime::new(year, month, day, hour, minute, second)
.map_err(|e| PyValueError::new_err(format!("Invalid UTCTime: {:?}", e)))?;
Ok(Self { inner })
}
#[getter]
fn year(&self) -> u16 {
self.inner.year
}
#[getter]
fn month(&self) -> u8 {
self.inner.month
}
#[getter]
fn day(&self) -> u8 {
self.inner.day
}
#[getter]
fn hour(&self) -> u8 {
self.inner.hour
}
#[getter]
fn minute(&self) -> u8 {
self.inner.minute
}
#[getter]
fn second(&self) -> u8 {
self.inner.second
}
fn __str__(&self) -> String {
self.inner.to_string()
}
fn __repr__(&self) -> String {
format!("UtcTime('{}')", self.inner)
}
}
impl std::fmt::Display for PyUtcTime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.inner)
}
}
#[pyclass(name = "GeneralizedTime")]
#[derive(Debug, Clone)]
pub struct PyGeneralizedTime {
pub(crate) inner: GeneralizedTime,
}
#[pymethods]
impl PyGeneralizedTime {
#[new]
fn new(
year: u16,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
milliseconds: Option<u16>,
) -> PyResult<Self> {
let inner = GeneralizedTime::new(year, month, day, hour, minute, second, milliseconds)
.map_err(|e| PyValueError::new_err(format!("Invalid GeneralizedTime: {:?}", e)))?;
Ok(Self { inner })
}
#[getter]
fn year(&self) -> u16 {
self.inner.year
}
#[getter]
fn month(&self) -> u8 {
self.inner.month
}
#[getter]
fn day(&self) -> u8 {
self.inner.day
}
#[getter]
fn hour(&self) -> u8 {
self.inner.hour
}
#[getter]
fn minute(&self) -> u8 {
self.inner.minute
}
#[getter]
fn second(&self) -> u8 {
self.inner.second
}
#[getter]
fn milliseconds(&self) -> Option<u16> {
self.inner.milliseconds
}
fn __str__(&self) -> String {
self.inner.to_string()
}
fn __repr__(&self) -> String {
format!("GeneralizedTime('{}')", self.inner)
}
}
impl std::fmt::Display for PyGeneralizedTime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.inner)
}
}
#[pyclass(name = "Real")]
#[derive(Debug, Clone, Copy)]
pub struct PyReal {
pub(crate) inner: Real,
}
#[pymethods]
impl PyReal {
#[new]
fn new(value: f64) -> Self {
Self {
inner: Real::new(value),
}
}
fn value(&self) -> f64 {
self.inner.value()
}
fn is_infinite(&self) -> bool {
self.inner.value().is_infinite()
}
fn is_nan(&self) -> bool {
self.inner.value().is_nan()
}
fn is_finite(&self) -> bool {
self.inner.value().is_finite()
}
fn __float__(&self) -> f64 {
self.inner.value()
}
fn __repr__(&self) -> String {
format!("Real({})", self.inner.value())
}
fn __str__(&self) -> String {
self.inner.value().to_string()
}
fn __eq__(&self, other: &Self) -> bool {
self.inner.value() == other.inner.value()
}
fn __hash__(&self, py: Python<'_>) -> PyResult<isize> {
self.inner.value().into_pyobject(py)?.hash()
}
}
#[pyclass(name = "Null")]
#[derive(Debug, Clone, Copy)]
pub struct PyNull;
#[pymethods]
impl PyNull {
#[new]
fn new() -> Self {
Self
}
fn __repr__(&self) -> String {
"Null()".to_string()
}
fn __eq__(&self, _other: &Self) -> bool {
true
}
fn __hash__(&self) -> isize {
0
}
}
#[pyclass(name = "Utf8String")]
#[derive(Debug, Clone)]
pub struct PyUtf8String {
pub(crate) inner: Utf8String,
}
#[pymethods]
impl PyUtf8String {
#[new]
fn new(value: &str) -> Self {
Self {
inner: Utf8String::new(value.to_string()),
}
}
fn as_str(&self) -> &str {
self.inner.as_str()
}
fn __str__(&self) -> &str {
self.inner.as_str()
}
fn __repr__(&self) -> String {
format!("Utf8String('{}')", self.inner.as_str())
}
fn __eq__(&self, other: &Self) -> bool {
self.inner == other.inner
}
fn __len__(&self) -> usize {
self.inner.as_str().chars().count()
}
}
#[pyclass(name = "PrintableString")]
#[derive(Debug, Clone)]
pub struct PyPrintableString {
pub(crate) inner: PrintableString,
}
#[pymethods]
impl PyPrintableString {
#[new]
fn new(value: &str) -> PyResult<Self> {
let inner = PrintableString::new(value.to_string())
.map_err(|e| PyValueError::new_err(format!("Invalid PrintableString: {:?}", e)))?;
Ok(Self { inner })
}
fn as_str(&self) -> &str {
self.inner.as_str()
}
fn __str__(&self) -> &str {
self.inner.as_str()
}
fn __repr__(&self) -> String {
format!("PrintableString('{}')", self.inner.as_str())
}
fn __eq__(&self, other: &Self) -> bool {
self.inner == other.inner
}
fn __len__(&self) -> usize {
self.inner.as_str().chars().count()
}
}
#[pyclass(name = "IA5String")]
#[derive(Debug, Clone)]
pub struct PyIA5String {
pub(crate) inner: IA5String,
}
#[pymethods]
impl PyIA5String {
#[new]
fn new(value: &str) -> PyResult<Self> {
let inner = IA5String::new(value.to_string())
.map_err(|e| PyValueError::new_err(format!("Invalid IA5String: {:?}", e)))?;
Ok(Self { inner })
}
fn as_str(&self) -> &str {
self.inner.as_str()
}
fn __str__(&self) -> &str {
self.inner.as_str()
}
fn __repr__(&self) -> String {
format!("IA5String('{}')", self.inner.as_str())
}
fn __eq__(&self, other: &Self) -> bool {
self.inner == other.inner
}
fn __len__(&self) -> usize {
self.inner.as_str().chars().count()
}
}
#[pyclass(name = "NumericString")]
#[derive(Debug, Clone)]
pub struct PyNumericString {
pub(crate) inner: NumericString,
}
#[pymethods]
impl PyNumericString {
#[new]
fn new(value: &str) -> PyResult<Self> {
let inner = NumericString::new(value.to_string())
.map_err(|e| PyValueError::new_err(format!("Invalid NumericString: {:?}", e)))?;
Ok(Self { inner })
}
fn as_str(&self) -> &str {
self.inner.as_str()
}
fn __str__(&self) -> &str {
self.inner.as_str()
}
fn __repr__(&self) -> String {
format!("NumericString('{}')", self.inner.as_str())
}
fn __eq__(&self, other: &Self) -> bool {
self.inner == other.inner
}
fn __len__(&self) -> usize {
self.inner.as_str().chars().count()
}
}
#[pyclass(name = "TeletexString")]
#[derive(Debug, Clone)]
pub struct PyTeletexString {
pub(crate) inner: TeletexString,
}
#[pymethods]
impl PyTeletexString {
#[new]
fn new(data: Vec<u8>) -> Self {
Self {
inner: TeletexString::new(data),
}
}
#[staticmethod]
fn from_latin1(value: &str) -> PyResult<Self> {
TeletexString::from_latin1(value)
.map(|inner| Self { inner })
.map_err(|e| PyValueError::new_err(format!("{:?}", e)))
}
#[staticmethod]
fn from_str(value: &str) -> PyResult<Self> {
TeletexString::from_latin1(value)
.map(|inner| Self { inner })
.map_err(|e| PyValueError::new_err(format!("{:?}", e)))
}
fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, self.inner.as_bytes())
}
fn as_str(&self) -> String {
self.inner.as_latin1_string()
}
fn __str__(&self) -> String {
self.inner.as_latin1_string()
}
fn __repr__(&self) -> String {
format!("TeletexString(<{} bytes>)", self.inner.as_bytes().len())
}
fn __len__(&self) -> usize {
self.inner.as_bytes().len()
}
fn __eq__(&self, other: &Self) -> bool {
self.inner == other.inner
}
}
#[pyclass(name = "VisibleString")]
#[derive(Debug, Clone)]
pub struct PyVisibleString {
pub(crate) inner: VisibleString,
}
#[pymethods]
impl PyVisibleString {
#[new]
fn new(value: &str) -> PyResult<Self> {
let inner = VisibleString::new(value.to_string())
.map_err(|e| PyValueError::new_err(format!("Invalid VisibleString: {:?}", e)))?;
Ok(Self { inner })
}
fn as_str(&self) -> &str {
self.inner.as_str()
}
fn __str__(&self) -> &str {
self.inner.as_str()
}
fn __repr__(&self) -> String {
format!("VisibleString('{}')", self.inner.as_str())
}
fn __eq__(&self, other: &Self) -> bool {
self.inner == other.inner
}
fn __len__(&self) -> usize {
self.inner.as_str().len()
}
}
#[pyclass(name = "GeneralString")]
#[derive(Debug, Clone)]
pub struct PyGeneralString {
pub(crate) inner: GeneralString,
}
#[pymethods]
impl PyGeneralString {
#[new]
fn new(data: Vec<u8>) -> Self {
Self {
inner: GeneralString::new(data),
}
}
#[staticmethod]
fn from_ascii(value: &str) -> PyResult<Self> {
GeneralString::from_ascii(value)
.map(|inner| Self { inner })
.map_err(|e| PyValueError::new_err(format!("{:?}", e)))
}
#[staticmethod]
fn from_str(value: &str) -> PyResult<Self> {
GeneralString::from_ascii(value)
.map(|inner| Self { inner })
.map_err(|e| PyValueError::new_err(format!("{:?}", e)))
}
fn to_bytes<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, self.inner.as_bytes())
}
fn as_str(&self) -> String {
self.inner.as_latin1_string()
}
fn __str__(&self) -> String {
self.inner.as_latin1_string()
}
fn __repr__(&self) -> String {
format!("GeneralString(<{} bytes>)", self.inner.as_bytes().len())
}
fn __len__(&self) -> usize {
self.inner.as_bytes().len()
}
fn __eq__(&self, other: &Self) -> bool {
self.inner == other.inner
}
}
#[pyclass(name = "UniversalString")]
#[derive(Debug, Clone)]
pub struct PyUniversalString {
pub(crate) inner: UniversalString,
}
#[pymethods]
impl PyUniversalString {
#[new]
fn new(value: &str) -> Self {
Self {
inner: UniversalString::new(value.to_string()),
}
}
#[staticmethod]
fn from_bytes(data: &[u8]) -> PyResult<Self> {
let inner = UniversalString::from_ucs4_be(data)
.map_err(|e| PyValueError::new_err(format!("Invalid UniversalString: {:?}", e)))?;
Ok(Self { inner })
}
fn as_str(&self) -> &str {
self.inner.as_str()
}
fn __str__(&self) -> &str {
self.inner.as_str()
}
fn __repr__(&self) -> String {
format!("UniversalString('{}')", self.inner.as_str())
}
fn __eq__(&self, other: &Self) -> bool {
self.inner == other.inner
}
fn __len__(&self) -> usize {
self.inner.as_str().chars().count()
}
}
#[pyclass(name = "BmpString")]
#[derive(Debug, Clone)]
pub struct PyBmpString {
pub(crate) inner: BmpString,
}
#[pymethods]
impl PyBmpString {
#[new]
fn new(value: &str) -> PyResult<Self> {
let inner = BmpString::new(value.to_string())
.map_err(|e| PyValueError::new_err(format!("Invalid BMPString: {:?}", e)))?;
Ok(Self { inner })
}
#[staticmethod]
fn from_bytes(data: &[u8]) -> PyResult<Self> {
let inner = BmpString::from_ucs2_be(data)
.map_err(|e| PyValueError::new_err(format!("Invalid BMPString: {:?}", e)))?;
Ok(Self { inner })
}
fn as_str(&self) -> &str {
self.inner.as_str()
}
fn __str__(&self) -> &str {
self.inner.as_str()
}
fn __repr__(&self) -> String {
format!("BmpString('{}')", self.inner.as_str())
}
fn __eq__(&self, other: &Self) -> bool {
self.inner == other.inner
}
fn __len__(&self) -> usize {
self.inner.as_str().chars().count()
}
}
#[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()
)
}
}
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(crate) 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())
}
}
}