use super::Role;
use crate::binary::{self, Location, OwnedBinaryXml, OwnedDecoded};
use crate::prepare::Prepared;
use crate::xml::{Access, AccessError, Snapshot, Xml};
use structured_asn1::{
Encoding, Reader,
memory::{MemoryError, Work},
};
use structured_cms::timestamp::Token;
pub struct TokenSource<N, D, G> {
location: Location<N, D, G>,
signature: N,
property: N,
role: Role,
stamp: usize,
ordinal: usize,
encoding: Encoding,
source: OwnedDecoded,
}
impl<N: Copy, D: Copy, G: Copy> TokenSource<N, D, G> {
#[must_use]
pub fn signature(&self) -> N {
self.signature
}
#[must_use]
pub fn location(&self) -> Location<N, D, G> {
self.location
}
#[must_use]
pub fn property(&self) -> N {
self.property
}
#[must_use]
pub fn role(&self) -> Role {
self.role
}
#[must_use]
pub fn stamp_index(&self) -> usize {
self.stamp
}
#[must_use]
pub fn token_index(&self) -> usize {
self.ordinal
}
#[must_use]
pub fn bytes(&self) -> &[u8] {
&self.source.bytes
}
}
pub struct TokenReading<'source> {
reader: Reader<'source>,
token: Token<'source>,
}
#[derive(Clone, Copy)]
pub struct TokenEntry<'view, 'source, N, D, G> {
pub(super) source: &'view TokenSource<N, D, G>,
pub(super) reading: &'view TokenReading<'source>,
}
impl<'source> TokenReading<'source> {
#[must_use]
pub fn reader(&self) -> &Reader<'source> {
&self.reader
}
#[must_use]
pub fn token(&self) -> &Token<'source> {
&self.token
}
}
pub struct TokenView<'source, N, D, G> {
sources: &'source [TokenSource<N, D, G>],
readings: &'source [TokenReading<'source>],
snapshot: Snapshot<D, G>,
signature: N,
}
impl<'source, N, D, G> TokenView<'source, N, D, G> {
#[must_use]
pub fn entry(&self, index: usize) -> Option<TokenEntry<'_, 'source, N, D, G>> {
Some(TokenEntry {
source: self.sources.get(index)?,
reading: self.readings.get(index)?,
})
}
#[must_use]
pub fn sources(&self) -> &'source [TokenSource<N, D, G>] {
self.sources
}
#[must_use]
pub fn readings(&self) -> &'source [TokenReading<'source>] {
self.readings
}
}
impl<'source, N: Copy, D: Copy, G: Copy> TokenView<'source, N, D, G> {
#[must_use]
pub fn snapshot(&self) -> Snapshot<D, G> {
self.snapshot
}
#[must_use]
pub fn signature(&self) -> N {
self.signature
}
pub(super) fn reborrow<'query>(&self) -> TokenView<'query, N, D, G>
where
'source: 'query,
{
TokenView {
sources: self.sources,
readings: self.readings,
snapshot: self.snapshot,
signature: self.signature,
}
}
}
#[derive(Debug)]
pub enum TokenPipelineError<E, VE, N, D, G> {
Foreign {
node: N,
},
Access(AccessError<E, N>),
Encoding(binary::Error<E, core::convert::Infallible, N, D, G>),
Reader {
location: Location<N, D, G>,
cause: structured_asn1::Error,
},
Token {
location: Location<N, D, G>,
cause: structured_cms::Error,
},
Resource {
node: N,
cause: MemoryError,
},
Visitor(VE),
}
pub type TokenPipelineResult<T, X, VE> = Result<
T,
TokenPipelineError<
<X as Xml>::Error,
VE,
<X as Xml>::Id,
<X as Xml>::Document,
<X as Xml>::Generation,
>,
>;
pub fn with_tokens<X: OwnedBinaryXml, R, VE>(
xml: &X,
prepared: &Prepared<X>,
operation: &Reader<'_>,
visitor: impl for<'source> FnOnce(
&Reader<'source>,
TokenView<'source, X::Id, X::Document, X::Generation>,
) -> Result<R, VE>,
) -> TokenPipelineResult<R, X, VE> {
with_inputs(xml, prepared, None, operation, visitor)
}
pub fn with_counter_tokens<X: OwnedBinaryXml, R, VE>(
xml: &X,
prepared: &Prepared<X>,
counters: &super::CounterTimestamps<X::Id, X::Document, X::Generation>,
operation: &Reader<'_>,
visitor: impl for<'source> FnOnce(
&Reader<'source>,
TokenView<'source, X::Id, X::Document, X::Generation>,
) -> Result<R, VE>,
) -> TokenPipelineResult<R, X, VE> {
with_inputs(xml, prepared, Some(counters), operation, visitor)
}
impl<N: Copy, D: Copy, G: Copy> TokenEntry<'_, '_, N, D, G> {
pub(super) fn reader_in<'query>(
&'query self,
operation: &Reader<'query>,
) -> structured_asn1::Result<Reader<'query>> {
self.source
.source
.reader_in(operation, self.source.encoding)
}
}
fn with_inputs<X: OwnedBinaryXml, R, VE>(
xml: &X,
prepared: &Prepared<X>,
counters: Option<&super::CounterTimestamps<X::Id, X::Document, X::Generation>>,
operation: &Reader<'_>,
visitor: impl for<'source> FnOnce(
&Reader<'source>,
TokenView<'source, X::Id, X::Document, X::Generation>,
) -> Result<R, VE>,
) -> TokenPipelineResult<R, X, VE> {
let layout = prepared.bound().containers();
let execution = operation.execution();
let access = Access::new(xml, layout.snapshot(), &execution);
admit(&access, layout.signature())?;
if let Some(counters) = counters
&& (counters.snapshot != layout.snapshot()
|| !access
.equal_id(counters.signature, layout.signature())
.map_err(|cause| TokenPipelineError::Resource {
node: layout.signature(),
cause,
})?)
{
return Err(TokenPipelineError::Foreign {
node: layout.signature(),
});
}
let mut sources = execution.vector();
let children = counters
.into_iter()
.flat_map(|counters| counters.entries.iter().map(super::CounterStamp::stamp));
let mut timestamps = prepared.timestamps().iter().chain(children).enumerate();
loop {
admit(&access, layout.signature())?;
let Some((stamp, timestamp)) = timestamps.next() else {
break;
};
for ordinal in 0..timestamp.tokens().len() {
admit(&access, timestamp.node())?;
let node = timestamp.tokens()[ordinal];
let location = Location {
snapshot: layout.snapshot(),
node,
};
let encoding = binary::selected_encoding(&access, location)
.map_err(TokenPipelineError::Encoding)?;
let source = binary::owned_source(xml, location, operation)
.map_err(TokenPipelineError::Access)?
.value;
sources
.try_push(TokenSource {
location,
signature: timestamp.signature,
property: timestamp.node(),
role: timestamp.role(),
stamp,
ordinal,
encoding,
source,
})
.map_err(|cause| TokenPipelineError::Resource { node, cause })?;
}
}
let mut readings = execution.vector();
for index in 0..sources.len() {
admit(&access, layout.signature())?;
let source = &sources[index];
let mut reader = source
.source
.reader_in(operation, source.encoding)
.map_err(|cause| TokenPipelineError::Reader {
location: source.location,
cause,
})?;
let token = Token::read_from(&mut reader).map_err(|cause| TokenPipelineError::Token {
location: source.location,
cause,
})?;
readings
.try_push(TokenReading { reader, token })
.map_err(|cause| TokenPipelineError::Resource {
node: source.location.node,
cause,
})?;
}
admit(&access, layout.signature())?;
let result = visitor(
operation,
TokenView {
sources: &sources,
readings: &readings,
snapshot: layout.snapshot(),
signature: layout.signature(),
},
)
.map_err(TokenPipelineError::Visitor)?;
admit(&access, layout.signature())?;
Ok(result)
}
fn admit<X: Xml, W: Work, VE>(
access: &Access<'_, X, W>,
node: X::Id,
) -> TokenPipelineResult<(), X, VE> {
access
.query(node, |_, _, work| Ok(work.charge_work(1)))
.map_err(TokenPipelineError::Access)?
.map_err(|cause| TokenPipelineError::Resource { node, cause })
}