use crate::Error;
use structured_asn1::memory::Work;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Refused;
pub trait Receiver {
fn write(&mut self, bytes: &[u8]) -> core::result::Result<(), Refused>;
}
pub trait Source {
type Error;
fn stream<W: Work>(
&mut self,
receiver: &mut impl Receiver,
execution: &W,
) -> core::result::Result<(), Self::Error>;
}
impl<const N: usize> Source for [&[u8]; N] {
type Error = RangesError;
fn stream<W: Work>(
&mut self,
receiver: &mut impl Receiver,
execution: &W,
) -> Result<(), Self::Error> {
for bytes in self.iter() {
execution.charge_work(1).map_err(RangesError::Resource)?;
receiver.write(bytes).map_err(RangesError::Receiver)?;
}
Ok(())
}
}
#[derive(Debug)]
pub enum RangesError {
Resource(structured_asn1::memory::MemoryError),
Receiver(Refused),
}
pub trait Sink {
type Error;
fn write<W: Work>(
&mut self,
bytes: &[u8],
execution: &W,
) -> core::result::Result<(), Self::Error>;
}
#[derive(Debug)]
pub enum StreamError<SE, CE> {
Source(SE),
Callback(CE),
Processing(Error),
}
enum Failure<E> {
Callback(E),
Processing(Error),
}
struct Guard<'a, S: Sink, W: Work> {
sink: &'a mut S,
execution: &'a W,
field: &'static str,
at: u64,
failure: Option<Failure<S::Error>>,
}
impl<S: Sink, W: Work> Receiver for Guard<'_, S, W> {
fn write(&mut self, bytes: &[u8]) -> core::result::Result<(), Refused> {
if self.failure.is_some() {
return Err(Refused);
}
if let Err(reason) = self.execution.charge_work(1) {
self.failure = Some(Failure::Processing(Error::resource(
reason, self.field, self.at,
)));
return Err(Refused);
}
if let Err(error) = self.sink.write(bytes, self.execution) {
self.failure = Some(Failure::Callback(error));
return Err(Refused);
}
Ok(())
}
}
pub fn run<S: Source, C: Sink, W: Work>(
source: &mut S,
sink: &mut C,
execution: &W,
field: &'static str,
at: u64,
) -> core::result::Result<(), StreamError<S::Error, C::Error>> {
execution
.charge_work(1)
.map_err(|reason| StreamError::Processing(Error::resource(reason, field, at)))?;
let mut guard = Guard {
sink,
execution,
field,
at,
failure: None,
};
let acquired = source.stream(&mut guard, execution);
match guard.failure {
Some(Failure::Callback(error)) => Err(StreamError::Callback(error)),
Some(Failure::Processing(error)) => Err(StreamError::Processing(error)),
None => acquired.map_err(StreamError::Source),
}
}