use crate::xml::{Access, AccessError, AccessResult, Name, Snapshot, Xml};
use structured_asn1::{
Encoding, Parser, Reader,
memory::{OwnedVec, Work},
};
use structured_cms::timestamp::Token;
pub trait BinaryXml: Xml {
fn with_octets<R, C, W: Work>(
&self,
snapshot: Snapshot<Self::Document, Self::Generation>,
node: Self::Id,
execution: &W,
visitor: impl for<'source> FnOnce(&'source [u8]) -> Result<R, C>,
) -> Result<Result<R, C>, Self::Error>;
fn with_binary<R, C>(
&self,
snapshot: Snapshot<Self::Document, Self::Generation>,
node: Self::Id,
operation: &Reader<'_>,
visitor: impl for<'source> FnOnce(Decoded<'source>) -> Result<R, C>,
) -> Result<Result<R, C>, Self::Error>;
}
pub trait OwnedBinaryXml: BinaryXml {
fn decode_owned<W: Work>(
&self,
snapshot: Snapshot<Self::Document, Self::Generation>,
node: Self::Id,
execution: &W,
) -> Result<OwnedVec<u8>, Self::Error>;
fn decode_binary_owned(
&self,
snapshot: Snapshot<Self::Document, Self::Generation>,
node: Self::Id,
operation: &Reader<'_>,
) -> Result<OwnedDecoded, Self::Error>;
}
#[derive(Debug)]
pub struct OwnedDecoded {
pub parser: Parser,
pub bytes: OwnedVec<u8>,
pub base: u64,
pub depth: u32,
}
impl OwnedDecoded {
#[must_use]
pub fn view(&self) -> Decoded<'_> {
Decoded {
parser: &self.parser,
bytes: &self.bytes,
base: self.base,
depth: self.depth,
}
}
pub fn reader_in<'source>(
&'source self,
operation: &Reader<'source>,
encoding: Encoding,
) -> structured_asn1::Result<Reader<'source>> {
self.parser
.read_query_in(operation, &self.bytes, self.base, encoding, self.depth)
}
}
#[derive(Debug, Clone, Copy)]
pub struct Decoded<'source> {
pub parser: &'source Parser,
pub bytes: &'source [u8],
pub base: u64,
pub depth: u32,
}
pub use structured_ltv::source::Location;
#[derive(Debug)]
pub struct Located<T, N, D, G> {
pub location: Location<N, D, G>,
pub value: T,
}
pub type LocatedValue<T, X> =
Located<T, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>;
pub fn owned<X: OwnedBinaryXml, W: Work>(
xml: &X,
location: Location<X::Id, X::Document, X::Generation>,
execution: &W,
) -> AccessResult<LocatedValue<OwnedVec<u8>, X>, X> {
let value = Access::new(xml, location.snapshot, execution)
.query(location.node, |xml, snapshot, work| {
xml.decode_owned(snapshot, location.node, work)
})?;
Ok(Located { location, value })
}
pub fn owned_source<X: OwnedBinaryXml>(
xml: &X,
location: Location<X::Id, X::Document, X::Generation>,
operation: &Reader<'_>,
) -> AccessResult<LocatedValue<OwnedDecoded, X>, X> {
let execution = operation.execution();
let value = Access::new(xml, location.snapshot, &execution)
.query(location.node, |xml, snapshot, _| {
xml.decode_binary_owned(snapshot, location.node, operation)
})?;
Ok(Located { location, value })
}
#[derive(Debug)]
pub enum Error<E, C, N, D, G> {
UnknownEncoding {
location: Location<N, D, G>,
},
UnsupportedEncoding {
location: Location<N, D, G>,
encoding: PkiEncoding,
},
ReaderCreation {
location: Location<N, D, G>,
cause: structured_asn1::Error,
},
Access {
location: Location<N, D, G>,
cause: AccessError<E, N>,
},
Callback {
location: Location<N, D, G>,
cause: C,
},
}
impl<E, C, N, D, G> Error<E, C, N, D, G> {
pub(crate) fn map_callback<T>(self, map: impl FnOnce(C) -> T) -> Error<E, T, N, D, G> {
match self {
Self::UnknownEncoding { location } => Error::UnknownEncoding { location },
Self::UnsupportedEncoding { location, encoding } => {
Error::UnsupportedEncoding { location, encoding }
}
Self::ReaderCreation { location, cause } => Error::ReaderCreation { location, cause },
Self::Access { location, cause } => Error::Access { location, cause },
Self::Callback { location, cause } => Error::Callback {
location,
cause: map(cause),
},
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PkiEncoding {
Der,
Ber,
Cer,
Per,
Xer,
}
fn pki_encoding<X: Xml, W: Work>(
access: &Access<'_, X, W>,
node: X::Id,
) -> core::result::Result<Option<PkiEncoding>, AccessError<X::Error, X::Id>> {
access
.attribute(
node,
Name {
namespace: "",
local: "Encoding",
},
|value| {
let Some(value) = value else {
return Ok(Some(PkiEncoding::Der));
};
let value = access.trim_xml_whitespace(value)?;
for (uri, encoding) in [
("http://uri.etsi.org/01903/v1.2.2#DER", PkiEncoding::Der),
("http://uri.etsi.org/01903/v1.2.2#BER", PkiEncoding::Ber),
("http://uri.etsi.org/01903/v1.2.2#CER", PkiEncoding::Cer),
("http://uri.etsi.org/01903/v1.2.2#PER", PkiEncoding::Per),
("http://uri.etsi.org/01903/v1.2.2#XER", PkiEncoding::Xer),
] {
if access.equal(value, uri)? {
return Ok(Some(encoding));
}
}
Ok(None)
},
)?
.map_err(|cause| AccessError::Resource { node, cause })
}
pub type BinaryResult<T, X, C> = core::result::Result<
T,
Error<<X as Xml>::Error, C, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>,
>;
pub fn with_octets<X: BinaryXml, W: Work, R, C>(
xml: &X,
location: Location<X::Id, X::Document, X::Generation>,
execution: &W,
visitor: impl for<'source> FnOnce(&'source [u8]) -> Result<R, C>,
) -> BinaryResult<R, X, C> {
Access::new(xml, location.snapshot, execution)
.query(location.node, |xml, snapshot, work| {
xml.with_octets(snapshot, location.node, work, visitor)
})
.map_err(|cause| Error::Access { location, cause })?
.map_err(|cause| Error::Callback { location, cause })
}
pub fn with_reader<X: BinaryXml, R, C>(
xml: &X,
location: Location<X::Id, X::Document, X::Generation>,
operation: &Reader<'_>,
visitor: impl for<'source> FnOnce(&mut Reader<'source>) -> core::result::Result<R, C>,
) -> BinaryResult<R, X, C> {
let execution = operation.execution();
let access = Access::new(xml, location.snapshot, &execution);
let encoding = selected_encoding(&access, location)
.map_err(|error| error.map_callback(|never| match never {}))?;
with_reader_as(xml, location, operation, encoding, visitor)
}
pub(crate) fn selected_encoding<X: Xml, W: Work>(
access: &Access<'_, X, W>,
location: Location<X::Id, X::Document, X::Generation>,
) -> BinaryResult<Encoding, X, core::convert::Infallible> {
let specified = pki_encoding(access, location.node)
.map_err(|cause| Error::Access { location, cause })?
.ok_or(Error::UnknownEncoding { location })?;
let encoding = match specified {
PkiEncoding::Der => Encoding::Der,
PkiEncoding::Ber => Encoding::Ber,
other => {
return Err(Error::UnsupportedEncoding {
location,
encoding: other,
});
}
};
Ok(encoding)
}
pub fn with_der_reader<X: BinaryXml, R, C>(
xml: &X,
location: Location<X::Id, X::Document, X::Generation>,
operation: &Reader<'_>,
visitor: impl for<'source> FnOnce(&mut Reader<'source>) -> Result<R, C>,
) -> BinaryResult<R, X, C> {
with_reader_as(xml, location, operation, Encoding::Der, visitor)
}
fn with_reader_as<X: BinaryXml, R, C>(
xml: &X,
location: Location<X::Id, X::Document, X::Generation>,
operation: &Reader<'_>,
encoding: Encoding,
visitor: impl for<'source> FnOnce(&mut Reader<'source>) -> Result<R, C>,
) -> BinaryResult<R, X, C> {
let execution = operation.execution();
let result = Access::new(xml, location.snapshot, &execution)
.query(location.node, |xml, snapshot, _| {
xml.with_binary(snapshot, location.node, operation, |source| {
let mut reader = source
.parser
.read_query_in(operation, source.bytes, source.base, encoding, source.depth)
.map_err(ScopedError::Processing)?;
visitor(&mut reader).map_err(ScopedError::Callback)
})
})
.map_err(|cause| Error::Access { location, cause })?;
result.map_err(|cause| match cause {
ScopedError::Processing(cause) => Error::ReaderCreation { location, cause },
ScopedError::Callback(cause) => Error::Callback { location, cause },
})
}
enum ScopedError<C> {
Processing(structured_asn1::Error),
Callback(C),
}
#[derive(Debug)]
pub enum TokenError<C> {
Processing(structured_cms::Error),
Callback(C),
}
pub fn with_token<X: BinaryXml, R, C>(
xml: &X,
location: Location<X::Id, X::Document, X::Generation>,
operation: &Reader<'_>,
visitor: impl for<'source> FnOnce(Token<'source>, &Reader<'source>) -> core::result::Result<R, C>,
) -> BinaryResult<R, X, TokenError<C>> {
with_reader(xml, location, operation, |reader| {
let token = Token::read_from(reader).map_err(TokenError::Processing)?;
visitor(token, reader).map_err(TokenError::Callback)
})
}