use crate::error::{CallbackError, CallbackResult, Error, ErrorCode, Located, Result};
use structured_asn1::{Element, Oid, ScopedReader, Span, Tag, universal};
#[derive(Debug, Clone, Copy)]
pub struct ExtensionHeader<'a> {
kind: Oid<'a>,
critical: bool,
critical_span: Option<Span>,
span: Span,
}
impl<'a> ExtensionHeader<'a> {
#[must_use]
pub fn kind(self) -> Oid<'a> {
self.kind
}
#[must_use]
pub fn is_critical(self) -> bool {
self.critical
}
#[must_use]
pub fn critical_span(self) -> Option<Span> {
self.critical_span
}
#[must_use]
pub fn span(self) -> Span {
self.span
}
#[must_use]
pub fn default_written(self) -> bool {
self.critical_span.is_some() && !self.critical
}
}
#[derive(Debug)]
pub struct ExtensionValue<'a, V> {
header: ExtensionHeader<'a>,
value: V,
trailing_span: Option<Span>,
}
impl<'a, V> ExtensionValue<'a, V> {
#[must_use]
pub fn header(&self) -> ExtensionHeader<'a> {
self.header
}
#[must_use]
pub fn value(&self) -> &V {
&self.value
}
#[must_use]
pub fn trailing_span(&self) -> Option<Span> {
self.trailing_span
}
#[must_use]
pub fn into_value(self) -> V {
self.value
}
}
pub fn read_with<'a: 'operation, 'operation, V, E>(
parent: &ScopedReader<'a, 'operation>,
element: &Element<'a>,
field: &'static str,
read_value: impl FnOnce(
&ExtensionHeader<'a>,
&ScopedReader<'a, 'operation>,
&Element<'a>,
) -> core::result::Result<V, E>,
) -> CallbackResult<ExtensionValue<'a, V>, E> {
open(parent, element, field)?.read_with(read_value)
}
pub struct ScopedExtensionFields<'a: 'operation, 'operation> {
header: ExtensionHeader<'a>,
inside: ScopedReader<'a, 'operation>,
pending: ValueState<'a>,
field: &'static str,
}
pub type ExtensionFields<'a> = ScopedExtensionFields<'a, 'a>;
enum ValueState<'a> {
Read(Option<Element<'a>>),
Deferred,
}
impl<'a: 'operation, 'operation> ScopedExtensionFields<'a, 'operation> {
#[must_use]
pub fn header(&self) -> ExtensionHeader<'a> {
self.header
}
#[must_use]
pub fn reader(&self) -> &ScopedReader<'a, 'operation> {
&self.inside
}
pub fn read_with<V, E>(
mut self,
read_value: impl FnOnce(
&ExtensionHeader<'a>,
&ScopedReader<'a, 'operation>,
&Element<'a>,
) -> core::result::Result<V, E>,
) -> CallbackResult<ExtensionValue<'a, V>, E> {
let value = match self.pending {
ValueState::Read(value) => value,
ValueState::Deferred => self.inside.next_element().field(self.field)?,
}
.ok_or_else(|| Error::new(ErrorCode::MissingField, self.field, self.header.span.start))?;
if value.tag() != Tag::primitive(universal::OCTET_STRING)
&& value.tag() != Tag::constructed(universal::OCTET_STRING)
{
return Err(Error::new(
ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag),
self.field,
value.span().start,
)
.into());
}
let trailing_span = if self.inside.is_empty() {
None
} else {
Some(Span {
start: self.inside.offset(),
length: self.inside.remaining().len() as u64,
})
};
let decoded =
read_value(&self.header, &self.inside, &value).map_err(CallbackError::Callback)?;
Ok(ExtensionValue {
header: self.header,
value: decoded,
trailing_span,
})
}
}
pub fn open<'a: 'operation, 'operation>(
parent: &ScopedReader<'a, 'operation>,
element: &Element<'a>,
field: &'static str,
) -> Result<ScopedExtensionFields<'a, 'operation>> {
let fail = |code, offset| Error::new(code, field, offset);
if element.tag() != Tag::constructed(universal::SEQUENCE) {
return Err(fail(
ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag),
element.span().start,
));
}
let mut inside = parent.enter(element).field(field)?;
let kind = inside.read_oid().field(field)?;
let value = inside.next_element().field(field)?;
let mut pending = ValueState::Read(value);
let mut critical_span = None;
let mut critical = false;
if let Some(flag) = value
&& flag.tag() == Tag::primitive(universal::BOOLEAN)
{
critical = inside.boolean(&flag).field(field)?;
critical_span = Some(flag.span());
pending = ValueState::Deferred;
}
Ok(ScopedExtensionFields {
header: ExtensionHeader {
kind,
critical,
critical_span,
span: element.span(),
},
inside,
pending,
field,
})
}