Skip to main content

sark_core/decode/
mod.rs

1//! Request decoding plans and response encoding contracts.
2use core::convert::Infallible;
3use std::ops::ControlFlow;
4
5use o3::buffer::{bytes::scope, pool, resident::bound, storage::Shared};
6use sark_http::{
7    field::{
8        metadata::{
9            packed,
10            section::{
11                self, rules,
12                state::{RetainedHead, Selected},
13                storage::Fault,
14            },
15        },
16        text,
17    },
18    method,
19    owner::handoff::{self, input::Bound, stage},
20    payload::response::{
21        dispatch::{Accept, Sink},
22        status::Code,
23    },
24};
25
26use crate::{body, decode::state::admit, request::frame::proof, route, route::field::header};
27
28mod call;
29pub mod dispatch;
30pub mod exec;
31pub mod request;
32pub mod state;
33
34pub use call::Call;
35
36/// Projected request coupled to the lifetime of its body borrow.
37#[repr(C)]
38pub struct Scoped<'req, 'd: 'req, D: Decode + ?Sized, Q: stage::Proof = ()>(
39    D::Projected<'req, 'd>,
40    handoff::Slot<'req, 'd, (), Bound<'req, 'd, Q>>,
41);
42
43impl<'req, 'd: 'req, D: Decode + ?Sized, Q: stage::Proof> Scoped<'req, 'd, D, Q> {
44    #[doc(hidden)]
45    pub const fn new(
46        value: D::Projected<'req, 'd>,
47        source: handoff::Slot<'req, 'd, (), Bound<'req, 'd, Q>>,
48    ) -> Self {
49        Self(value, source)
50    }
51
52    pub fn into_inner(self) -> D::Projected<'req, 'd> {
53        self.0
54    }
55
56    pub fn into_parts(
57        self,
58    ) -> (
59        D::Projected<'req, 'd>,
60        handoff::Slot<'req, 'd, (), Bound<'req, 'd, Q>>,
61    ) {
62        (self.0, self.1)
63    }
64}
65
66/// Ownership of request-head storage while a response body may retain it.
67pub trait Lineage<'id>: Sized {
68    type Output;
69
70    fn resolve_head<O: Into<scope::Owner<'id, Shared>>, P: proof::Proof>(
71        self,
72        head: crate::request::Head<O, P>,
73    ) -> Self::Output;
74}
75
76/// Ends response preparation and returns its exact failure target.
77pub trait Cancel {
78    type Target;
79
80    fn cancel(self) -> Self::Target;
81}
82
83pub trait Encode<'req, 'd: 'req>:
84    Cancel
85    + Accept<
86        'd,
87        Output = <Self as Encode<'req, 'd>>::Prepared,
88        Error = (<Self as Encode<'req, 'd>>::Error, Self::Target),
89    >
90{
91    type Error;
92    type Prepared;
93    type Pending<'id>: Lineage<'id, Output = Self::Prepared>;
94    type Head<'a, 'id>: Accept<
95            'd,
96            Output = Self::Pending<'id>,
97            Error = (<Self as Encode<'req, 'd>>::Error, Self::Target),
98        > + Cancel<Target = Self::Target>;
99
100    fn begin_head<'a, 'id>(self, head: scope::View<'a, 'id>) -> Self::Head<'a, 'id>;
101}
102
103/// Separates an encoded head from the body retained by a streaming task.
104pub trait Streaming<'req, 'd: 'req>: Encode<'req, 'd> + Sink<'d> {
105    fn split_stream<S>(output: Self::Stream<S>) -> (Self::Prepared, Option<S>);
106}
107
108/// Terminal semantic failure while preparing a request head.
109#[derive(PartialEq, Eq)]
110#[repr(u8)]
111#[derive(Debug)]
112pub enum HeadError<C = Infallible> {
113    BadRequest,
114    ContentLength(text::IntError),
115    Method(method::Error),
116    Bind(crate::request::BindError),
117    Storage(Fault),
118    Slot(header::Tag),
119    NotFound,
120    Schema(C),
121}
122
123impl<C> HeadError<C> {
124    pub const fn status(&self) -> Code {
125        match self {
126            Self::BadRequest
127            | Self::ContentLength(_)
128            | Self::Method(method::Error::Syntax(_))
129            | Self::Bind(_)
130            | Self::Storage(_)
131            | Self::Slot(_)
132            | Self::Schema(_) => Code::BAD_REQUEST,
133            Self::NotFound => Code::NOT_FOUND,
134            Self::Method(method::Error::Allocation(_)) => Code::INTERNAL_SERVER_ERROR,
135        }
136    }
137}
138
139pub trait Decode {
140    type Prepared: request::Prepared;
141    type Routes: state::Family<Self::Prepared>;
142    type Head: request::Head<
143            Prepared = Self::Prepared,
144            Start: request::Selection<
145                Self::Prepared,
146                Guard = Self::Routes,
147                Routed = <Self::Routes as state::Family<Self::Prepared>>::State<state::Routed>,
148            >,
149        >;
150    type Plan: rules::Policy;
151    type Cause;
152    type Error<'req>;
153    type Projected<'req, 'd>
154    where
155        'd: 'req;
156
157    /// Response backing bound; executable adapters require it to outlive their driver.
158    type Egress<'d>: bound::Bound;
159
160    fn reject<'req>(error: Self::Error<'req>) -> route::Reject;
161
162    /// Routes an admitted request while retaining every decoded field.
163    fn try_full(
164        &self,
165        head: RetainedHead<packed::Decoded, Selected<rules::RetainRaw>>,
166    ) -> Result<Self::Head, HeadError<Self::Cause>>;
167
168    fn try_head(
169        &self,
170        head: RetainedHead<<Self::Plan as section::Policy>::Block, Selected<Self::Plan>>,
171    ) -> Result<Self::Head, HeadError<Self::Cause>>;
172
173    fn try_project<'d, I, T, F>(
174        &self,
175        prepared: Self::Prepared,
176        body: body::Input<I>,
177        project: F,
178    ) -> body::Projection<T, I::Owner, (Self::Prepared, body::Input<I>)>
179    where
180        I: handoff::Project<'d>,
181        F: for<'req> FnOnce(Scoped<'req, 'd, Self, I::Proof>) -> T;
182
183    fn try_dispatch<'req, 'd, E: Encode<'req, 'd>>(
184        &self,
185        request: Self::Projected<'req, 'd>,
186        encoder: E,
187    ) -> Dispatched<'req, 'd, E, Self::Error<'req>>
188    where
189        'd: 'req;
190}
191
192/// Routes a native raw head while preserving its compile-time storage prefix.
193pub trait Full: Decode {
194    fn try_prefixed<const PREFIX: usize>(
195        &self,
196        head: RetainedHead<packed::Decoded<pool::Span, PREFIX>, Selected<rules::RetainRaw<PREFIX>>>,
197    ) -> Result<Self::Head, HeadError<Self::Cause>>;
198}
199
200/// Decoder capability implemented only by applications with guarded routes.
201pub trait Guarded: Decode {
202    fn try_admit<'req, 'd: 'req, E, F, G, V>(
203        &self,
204        request: request::Guarded<<Self::Routes as state::Family<Self::Prepared>>::State<F>>,
205        encoder: E,
206        visit: &mut V,
207    ) -> Admission<'req, 'd, Self, E, F, G, V>
208    where
209        E: Encode<'req, 'd>,
210        F: state::Set,
211        G: state::Set,
212        V: admit::TryVisit<Self::Prepared, F, G, <E as Encode<'req, 'd>>::Error>;
213}
214
215type Dispatched<'req, 'd, E, F> = Result<
216    <E as Encode<'req, 'd>>::Prepared,
217    (
218        dispatch::Error<<E as Encode<'req, 'd>>::Error, Infallible, F>,
219        <E as Cancel>::Target,
220    ),
221>;
222
223type Admission<'req, 'd, D, E, F, G, V> = ControlFlow<
224    <V as admit::TryVisit<<D as Decode>::Prepared, F, G, <E as Encode<'req, 'd>>::Error>>::Break<
225        <E as Encode<'req, 'd>>::Prepared,
226        <E as Cancel>::Target,
227    >,
228    <<<D as Decode>::Prepared as request::Prepared>::Family as state::Family<
229        <D as Decode>::Prepared,
230    >>::State<G>,
231>;