Skip to main content

sark_core/duplex/
mod.rs

1//! Protocol-neutral contracts for accepted byte streams.
2use core::convert::Infallible;
3type Opened<S, B, E> = Result<(S, B), (OpenError<E>, B)>;
4use sark_http::{field::metadata::block, payload::owned};
5
6pub mod admit;
7pub mod input;
8pub mod machine;
9
10use crate::{
11    decode,
12    decode::{exec, request::Admitted},
13    request, route,
14};
15
16/// Response-shape marker for a route which establishes a duplex byte stream.
17pub enum Shape {}
18
19/// Failure before a carrier committed its successful establishment response.
20#[repr(u8)]
21pub enum OpenError<E> {
22    InvalidRequest(E),
23    Length,
24    Unsupported,
25}
26
27/// Directional disposition causally adjacent to one session action.
28#[derive(Clone, Copy, PartialEq, Eq)]
29#[repr(u8)]
30pub enum Directive {
31    Wait,
32    Activate,
33    FinishOutput,
34    FinishSession,
35}
36
37#[must_use = "return this output action to the duplex machine"]
38pub struct Action<'d> {
39    output: Option<owned::Segments<'d>>,
40    directive: Directive,
41}
42
43impl<'d> Action<'d> {
44    pub const fn idle() -> Self {
45        Self {
46            output: None,
47            directive: Directive::Wait,
48        }
49    }
50
51    pub fn send(output: impl Into<owned::Segments<'d>>) -> Self {
52        Self {
53            output: Some(output.into()),
54            directive: Directive::Wait,
55        }
56    }
57
58    pub const fn end() -> Self {
59        Self {
60            output: None,
61            directive: Directive::FinishOutput,
62        }
63    }
64
65    pub fn send_end(output: impl Into<owned::Segments<'d>>) -> Self {
66        Self {
67            output: Some(output.into()),
68            directive: Directive::FinishOutput,
69        }
70    }
71
72    pub const fn close() -> Self {
73        Self {
74            output: None,
75            directive: Directive::FinishSession,
76        }
77    }
78
79    pub fn send_close(output: impl Into<owned::Segments<'d>>) -> Self {
80        Self {
81            output: Some(output.into()),
82            directive: Directive::FinishSession,
83        }
84    }
85
86    pub const fn activate(mut self) -> Self {
87        if matches!(self.directive, Directive::Wait | Directive::Activate) {
88            self.directive = Directive::Activate;
89        }
90        self
91    }
92
93    fn into_parts(self) -> (Option<owned::Segments<'d>>, Directive) {
94        (self.output, self.directive)
95    }
96}
97
98/// Established byte-stream owner retained without type erasure.
99pub trait Session<'d> {
100    fn receive(
101        &mut self,
102        input: owned::Segments<'d>,
103        output: &mut [u8],
104        scratch: &mut Vec<u8>,
105    ) -> (usize, Action<'d>);
106
107    /// Observes the peer's orderly end of the input direction exactly once.
108    fn finish(&mut self, output: &mut [u8], scratch: &mut Vec<u8>) -> (usize, Action<'d>);
109
110    fn activate(&mut self, output: &mut [u8], scratch: &mut Vec<u8>) -> (usize, Action<'d>);
111}
112
113/// Uninhabited session used by generated applications with no duplex routes.
114pub enum Never {}
115
116impl<'d> Session<'d> for Never {
117    fn receive(
118        &mut self,
119        _: owned::Segments<'d>,
120        _: &mut [u8],
121        _: &mut Vec<u8>,
122    ) -> (usize, Action<'d>) {
123        match *self {}
124    }
125
126    fn finish(&mut self, _: &mut [u8], _: &mut Vec<u8>) -> (usize, Action<'d>) {
127        match *self {}
128    }
129
130    fn activate(&mut self, _: &mut [u8], _: &mut Vec<u8>) -> (usize, Action<'d>) {
131        match *self {}
132    }
133}
134
135/// Exact duplex constructor consumed by multiplexed carriers.
136pub trait Application<'d>: decode::Decode {
137    type Duplex: Session<'d>;
138
139    /// Extended CONNECT capability of the route language.
140    const CONNECT: block::Connect = block::Connect::Disabled;
141
142    fn try_open<B>(
143        &self,
144        req: Admitted<exec::Duplex, Self::Prepared, B>,
145    ) -> Opened<Self::Duplex, B, Self::Error<'d>>;
146}
147
148/// Synchronous admission whose session depends only on the state lifetime.
149pub trait Route<'state, State>: route::Spec<Kind = Shape> {
150    type Session: 'state;
151
152    fn open<'req, 'd>(
153        params: <Self::Request as route::Project>::Params<'req>,
154        req: &request::View<'req, 'd, <Self::Request as route::Schema>::Frame>,
155        head: <Self::Request as route::Project>::Headers<'req>,
156        body: <Self::Request as route::Project>::ParsedBody<'req, 'd>,
157        state: &'state State,
158    ) -> Self::Session
159    where
160        'd: 'req + 'state,
161        Self::Session: Session<'d>;
162}
163
164const _: () = {
165    assert!(core::mem::size_of::<OpenError<Infallible>>() == core::mem::size_of::<u8>());
166    assert!(
167        core::mem::size_of::<Action<'static>>()
168            <= core::mem::size_of::<Option<owned::Segments<'static>>>()
169                + core::mem::size_of::<usize>()
170    );
171};