use crate::{
Budget,
executor::Resources,
input::{EncodingError, Input},
program::{Program, ProgramError},
};
#[derive(Clone, Copy, Debug)]
pub enum Subject<'a> {
Wtf8(&'a [u8]),
Utf16(&'a [u16]),
}
pub trait ImmutableSubject {
type Error;
fn with_subject<T>(&self, use_subject: impl FnOnce(Subject<'_>) -> T)
-> Result<T, Self::Error>;
}
pub trait ImmutableProgram {
type Error;
fn with_words<T>(&self, use_words: impl FnOnce(&[u32]) -> T) -> Result<T, Self::Error>;
}
impl ImmutableSubject for [u8] {
type Error = core::convert::Infallible;
fn with_subject<T>(&self, f: impl FnOnce(Subject<'_>) -> T) -> Result<T, Self::Error> {
Ok(f(Subject::Wtf8(self)))
}
}
impl ImmutableSubject for [u16] {
type Error = core::convert::Infallible;
fn with_subject<T>(&self, f: impl FnOnce(Subject<'_>) -> T) -> Result<T, Self::Error> {
Ok(f(Subject::Utf16(self)))
}
}
impl ImmutableProgram for [u32] {
type Error = core::convert::Infallible;
fn with_words<T>(&self, f: impl FnOnce(&[u32]) -> T) -> Result<T, Self::Error> {
Ok(f(self))
}
}
impl<S: ImmutableSubject + ?Sized> ImmutableSubject for &S {
type Error = S::Error;
fn with_subject<T>(&self, f: impl FnOnce(Subject<'_>) -> T) -> Result<T, Self::Error> {
S::with_subject(self, f)
}
}
impl<P: ImmutableProgram + ?Sized> ImmutableProgram for &P {
type Error = P::Error;
fn with_words<T>(&self, f: impl FnOnce(&[u32]) -> T) -> Result<T, Self::Error> {
P::with_words(self, f)
}
}
#[derive(Debug)]
pub enum SubjectError<E> {
Resource(E),
Encoding(EncodingError),
ChangedLayout,
}
#[derive(Debug)]
pub enum BoundProgramError<E> {
Resource(E),
Validation(ProgramError),
ChangedLayout,
}
#[derive(Debug)]
pub struct BindingError<S, E> {
pub storage: S,
pub error: E,
}
pub struct BoundSubject<S: ImmutableSubject> {
storage: S,
layout: (u8, usize, usize),
}
impl<S: ImmutableSubject> BoundSubject<S> {
pub fn new(storage: S) -> Result<Self, BindingError<S, SubjectError<S::Error>>> {
let result = storage
.with_subject(|subject| match subject {
Subject::Wtf8(bytes) => Input::wtf8(bytes).map(Input::shape),
Subject::Utf16(units) => Ok(Input::utf16(units).shape()),
})
.map_err(SubjectError::Resource)
.and_then(|result| result.map_err(SubjectError::Encoding));
match result {
Ok(layout) => Ok(Self { storage, layout }),
Err(error) => Err(BindingError { storage, error }),
}
}
pub fn with_view<T>(
&self,
f: impl FnOnce(Input<'_>) -> T,
) -> Result<T, SubjectError<S::Error>> {
self.storage
.with_subject(|subject| {
let input = match subject {
Subject::Wtf8(bytes) => Input::reborrow_bytes(bytes, self.layout),
Subject::Utf16(units) => {
let input = Input::utf16(units);
(input.shape() == self.layout).then_some(input)
}
}
.ok_or(SubjectError::ChangedLayout)?;
Ok(f(input))
})
.map_err(SubjectError::Resource)?
}
pub fn into_storage(self) -> S {
self.storage
}
}
pub struct BoundProgram<P: ImmutableProgram> {
storage: P,
header: [u32; crate::program::HEADER],
words: usize,
}
impl<P: ImmutableProgram> BoundProgram<P> {
pub fn new(
storage: P,
budget: &mut Budget,
) -> Result<Self, BindingError<P, BoundProgramError<P::Error>>> {
let result = storage
.with_words(|words| {
Program::from_words(words, budget).map(|p| {
(
p.words[..crate::program::HEADER].try_into().unwrap(),
words.len(),
)
})
})
.map_err(BoundProgramError::Resource)
.and_then(|result| result.map_err(BoundProgramError::Validation));
match result {
Ok((header, words)) => Ok(Self {
storage,
header,
words,
}),
Err(error) => Err(BindingError { storage, error }),
}
}
pub fn with_view<T>(
&self,
f: impl FnOnce(Program<'_>) -> T,
) -> Result<T, BoundProgramError<P::Error>> {
self.storage
.with_words(|words| {
if words.len() != self.words || words.get(..self.header.len()) != Some(&self.header)
{
return Err(BoundProgramError::ChangedLayout);
}
Ok(f(Program { words }))
})
.map_err(BoundProgramError::Resource)?
}
pub fn into_storage(self) -> P {
self.storage
}
}
#[derive(Debug)]
pub enum PairError<P, S> {
Program(BoundProgramError<P>),
Subject(SubjectError<S>),
}
pub struct BoundResources<'a, P: ImmutableProgram, S: ImmutableSubject> {
pub program: &'a BoundProgram<P>,
pub subject: &'a BoundSubject<S>,
}
impl<P: ImmutableProgram, S: ImmutableSubject> Resources for BoundResources<'_, P, S> {
type Error = PairError<P::Error, S::Error>;
fn with_views<T>(&self, f: impl FnOnce(Program<'_>, Input<'_>) -> T) -> Result<T, Self::Error> {
self.program
.with_view(|p| {
self.subject
.with_view(|input| f(p, input))
.map_err(PairError::Subject)
})
.map_err(PairError::Program)?
}
}