use core::convert::Infallible;
type Opened<S, B, E> = Result<(S, B), (OpenError<E>, B)>;
use sark_http::{field::metadata::block, payload::owned};
pub mod admit;
pub mod input;
pub mod machine;
use crate::{
decode,
decode::{exec, request::Admitted},
request, route,
};
pub enum Shape {}
#[repr(u8)]
pub enum OpenError<E> {
InvalidRequest(E),
Length,
Unsupported,
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Directive {
Wait,
Activate,
FinishOutput,
FinishSession,
}
#[must_use = "return this output action to the duplex machine"]
pub struct Action<'d> {
output: Option<owned::Segments<'d>>,
directive: Directive,
}
impl<'d> Action<'d> {
pub const fn idle() -> Self {
Self {
output: None,
directive: Directive::Wait,
}
}
pub fn send(output: impl Into<owned::Segments<'d>>) -> Self {
Self {
output: Some(output.into()),
directive: Directive::Wait,
}
}
pub const fn end() -> Self {
Self {
output: None,
directive: Directive::FinishOutput,
}
}
pub fn send_end(output: impl Into<owned::Segments<'d>>) -> Self {
Self {
output: Some(output.into()),
directive: Directive::FinishOutput,
}
}
pub const fn close() -> Self {
Self {
output: None,
directive: Directive::FinishSession,
}
}
pub fn send_close(output: impl Into<owned::Segments<'d>>) -> Self {
Self {
output: Some(output.into()),
directive: Directive::FinishSession,
}
}
pub const fn activate(mut self) -> Self {
if matches!(self.directive, Directive::Wait | Directive::Activate) {
self.directive = Directive::Activate;
}
self
}
fn into_parts(self) -> (Option<owned::Segments<'d>>, Directive) {
(self.output, self.directive)
}
}
pub trait Session<'d> {
fn receive(&mut self, input: owned::Segments<'d>) -> Action<'d>;
fn finish(&mut self) -> Action<'d>;
fn activate(&mut self) -> Action<'d>;
}
pub enum Never {}
impl<'d> Session<'d> for Never {
fn receive(&mut self, _: owned::Segments<'d>) -> Action<'d> {
match *self {}
}
fn finish(&mut self) -> Action<'d> {
match *self {}
}
fn activate(&mut self) -> Action<'d> {
match *self {}
}
}
pub trait Application<'d>: decode::Decode {
type Duplex: Session<'d>;
const CONNECT: block::Connect = block::Connect::Disabled;
fn try_open<B>(
&self,
req: Admitted<exec::Duplex, Self::Prepared, B>,
) -> Opened<Self::Duplex, B, Self::Error<'d>>;
}
pub trait Route<'state, State>: route::Spec<Kind = Shape> {
type Session: 'state;
fn open<'req, 'd>(
params: <Self::Request as route::Project>::Params<'req>,
req: &request::View<'req, 'd, <Self::Request as route::Schema>::Frame>,
head: <Self::Request as route::Project>::Headers<'req>,
body: <Self::Request as route::Project>::ParsedBody<'req, 'd>,
state: &'state State,
) -> Self::Session
where
'd: 'req + 'state,
Self::Session: Session<'d>;
}
const _: () = {
assert!(core::mem::size_of::<OpenError<Infallible>>() == core::mem::size_of::<u8>());
assert!(
core::mem::size_of::<Action<'static>>()
<= core::mem::size_of::<Option<owned::Segments<'static>>>()
+ core::mem::size_of::<usize>()
);
};