Skip to main content

sark_core/route/
mod.rs

1use sark_http::{payload::response::dispatch, request::Id};
2
3use crate::{
4    body::{
5        self,
6        admit::{Retention, Stream},
7    },
8    request,
9    request::frame::proof,
10};
11
12mod error;
13mod input;
14mod never;
15mod reject;
16
17pub use error::Error;
18pub use input::Input;
19pub use never::Never;
20pub use reject::Reject;
21pub mod field;
22pub mod guard;
23pub mod raw;
24pub mod table;
25
26use field::{header, query};
27
28/// Fiber-free, cooperatively stepped construction of an owned request.
29pub trait TaskBuild<'d>: body::Build<'d, Output = Built<'d, Self::Task>> {
30    type Task;
31
32    fn reject(error: Self::StepError) -> Reject;
33}
34
35/// Capability to transfer retained body ownership into a task builder.
36pub trait Push<'d>: TaskBuild<'d> + body::Push<'d> {
37    fn reject_push(error: Self::Error) -> Reject;
38}
39
40/// A completed owned request paired with its ingress identity.
41pub struct Built<'d, T> {
42    id: Id<'d>,
43    request: T,
44}
45
46impl<'d, T> Built<'d, T> {
47    #[must_use]
48    pub const fn new(id: Id<'d>, request: T) -> Self {
49        Self { id, request }
50    }
51
52    #[must_use]
53    pub fn into_parts(self) -> (Id<'d>, T) {
54        (self.id, self.request)
55    }
56}
57
58/// Wire-level request schema shared by every transport and execution mode.
59pub trait Schema: Sized {
60    type Header: header::Policy<Self> + header::Fields<Self>;
61    type Query: query::Fields<Self>;
62    type RawHeaders: Copy + Default;
63    type Error;
64    type RawParams: RawParams;
65    type Body: body::Project;
66    type Frame: proof::Proof;
67
68    const FULL: bool = false;
69    /// Whether the generated handler projects the effective request
70    /// authority and therefore requires compressed protocols to retain it.
71    const AUTHORITY: bool = false;
72    const BODY_RETENTION: Retention = <Self::Body as body::Project>::RETENTION;
73}
74
75pub trait Fields: Schema<Error = Error<Self::Identity>> {
76    type Identity: Copy;
77}
78
79/// Borrowed request projection used by synchronous and duplex routes.
80pub trait Project: Schema {
81    type Params<'req>;
82    type Headers<'req>;
83    type Error<'req>;
84    type View<'req, 'd>
85    where
86        'd: 'req;
87    type ParsedBody<'req, 'd>
88    where
89        'd: 'req;
90
91    fn reject<'req>(error: <Self as Project>::Error<'req>) -> Reject;
92
93    fn from_params<'req, 'body>(
94        request: &request::raw::Parsed<request::Head<&'req [u8], Self::Frame>, Self>,
95    ) -> Result<Self::Params<'req>, <Self as Project>::Error<'body>>;
96
97    fn parse_body<'req, 'd>(
98        body: <Self::Body as body::Project>::View<'req, 'd>,
99    ) -> Result<Self::ParsedBody<'req, 'd>, <Self as Project>::Error<'req>>
100    where
101        'd: 'req;
102
103    fn from_parts<'req, 'd>(
104        params: Self::Params<'req>,
105        headers: Self::Headers<'req>,
106        body: Self::ParsedBody<'req, 'd>,
107        request: &request::View<'req, 'd, Self::Frame>,
108    ) -> Self::View<'req, 'd>
109    where
110        'd: 'req;
111
112    fn from_headers<'req, 'body>(
113        request: &request::raw::Parsed<request::Head<&'req [u8], Self::Frame>, Self>,
114    ) -> Result<Self::Headers<'req>, <Self as Project>::Error<'body>>;
115}
116
117/// Body-input strategy selected by an owned request schema.
118pub trait Mode: sealed::Mode {
119    const STREAM: Stream;
120}
121
122/// The builder consumes segments before the complete message body arrives.
123#[derive(Debug)]
124pub struct Streaming;
125
126/// The builder requires the complete body before construction.
127#[derive(Debug)]
128pub struct Buffered;
129
130impl Mode for Streaming {
131    const STREAM: Stream = Stream::Streaming;
132}
133
134impl Mode for Buffered {
135    const STREAM: Stream = Stream::Buffered;
136}
137
138mod sealed {
139    pub trait Mode {}
140
141    impl Mode for super::Streaming {}
142    impl Mode for super::Buffered {}
143}
144
145/// Retained request construction used by cooperatively scheduled routes.
146pub trait Own: Schema {
147    type Mode: Mode;
148
149    /// User-visible request value backed by retained ingress ownership.
150    type Task<'d>;
151    type Error<'d>;
152    type TaskBuilder<'d>: TaskBuild<'d, Task = Self::Task<'d>>;
153
154    /// Optionally constructs an input-independent request before task admission.
155    /// Return `None` when construction or destruction has observable effects.
156    fn empty_task<'d>(_: Id<'d>) -> Option<Built<'d, Self::Task<'d>>> {
157        None
158    }
159
160    fn reject<'d>(error: <Self as Own>::Error<'d>) -> Reject;
161
162    fn start_task<'d>(
163        request: request::raw::Parsed<request::Retained<'d, Self::Frame>, Self>,
164    ) -> Result<Self::TaskBuilder<'d>, <Self as Own>::Error<'d>>;
165
166    fn start_stream<'d>(
167        request: request::raw::Parsed<request::Parts<'d, Self::Frame>, Self>,
168    ) -> Result<Self::TaskBuilder<'d>, <Self as Own>::Error<'d>>;
169}
170
171pub trait RawParams: Copy {
172    type Captures;
173
174    /// Canonically sorted request path-field names.
175    const PATH_NAMES: &'static [&'static [u8]];
176}
177
178/// Raw path fields constructed directly from a trusted route match.
179pub trait Params: RawParams {
180    fn from_match(captures: Self::Captures) -> Self;
181}
182
183impl RawParams for () {
184    type Captures = ();
185    const PATH_NAMES: &'static [&'static [u8]] = &[];
186}
187
188pub trait Spec {
189    type Kind;
190
191    type Request: Project;
192    type Stream<'d>;
193    const MAX_BODY: usize = 1024 * 1024;
194}
195
196/// A route whose task retains its response until driver-local completion.
197pub trait Task<'d, S>: Spec {
198    type Output: dispatch::IntoOutput<'d, 'd>;
199}