Skip to main content

sark_core/body/
stream.rs

1use core::num::NonZero;
2
3use sark_http::payload::owned;
4
5use crate::{
6    body::{
7        self, admit, credit, flow, ingress,
8        input::{self, Parts},
9    },
10    manifold::stream::Boundary,
11};
12
13/// Incremental builder and the exact accounting state of its input.
14pub struct Stream<'d, B: body::Build<'d>, O: credit::Origin = credit::raw::Stored> {
15    pub(in crate::body) state: ingress::State<O>,
16    pub(in crate::body) flow: flow::Flow<'d, B>,
17}
18
19/// Input ownership after one incremental stream transition.
20#[must_use = "consume returned input owners or continue with the successor stream"]
21pub enum Push<D, B, E> {
22    Accepted(B),
23    Discarded {
24        build: B,
25        input: D,
26    },
27    Refused {
28        build: B,
29        input: D,
30        error: body::Error,
31    },
32    Failed {
33        build: B,
34        error: body::Error,
35    },
36    Rejected {
37        build: B,
38        error: E,
39    },
40}
41
42impl<D, B, E> Push<D, B, E> {
43    /// Changes only the builder owner, retaining the exact input and failure.
44    pub fn map<C>(self, map: impl FnOnce(B) -> C) -> Push<D, C, E> {
45        match self {
46            Self::Accepted(build) => Push::Accepted(map(build)),
47            Self::Discarded { build, input } => Push::Discarded {
48                build: map(build),
49                input,
50            },
51            Self::Refused {
52                build,
53                input,
54                error,
55            } => Push::Refused {
56                build: map(build),
57                input,
58                error,
59            },
60            Self::Failed { build, error } => Push::Failed {
61                build: map(build),
62                error,
63            },
64            Self::Rejected { build, error } => Push::Rejected {
65                build: map(build),
66                error,
67            },
68        }
69    }
70}
71
72/// The exact owner produced by one cooperative construction and admission turn.
73#[must_use = "consume the output or retain the successor stream"]
74pub enum Drive<'d, B: body::Build<'d>, O: credit::Origin = credit::raw::Stored> {
75    NeedInput(Stream<'d, B, O>),
76    Pending(Stream<'d, B, O>),
77    Ready(flow::Ready<'d, B, O>),
78    Failed {
79        stream: Stream<'d, B, O>,
80        error: flow::Error<B::StepError>,
81    },
82    Rejected {
83        stream: Stream<'d, B, O>,
84        error: body::Error,
85    },
86}
87
88#[must_use = "consume the admitted output or retain the rejected ready owner"]
89pub enum Admission<'d, B: body::Build<'d>, E, O: credit::Origin = credit::raw::Stored> {
90    Accepted(B::Output),
91    Rejected {
92        ready: flow::Ready<'d, B, O>,
93        evidence: E,
94        error: body::Error,
95    },
96}
97
98impl<'d, B: body::Build<'d>> Stream<'d, B> {
99    pub fn new(open: admit::Track<admit::Open>, build: B) -> Self {
100        Self {
101            state: ingress::State::new(admit::Start::Open(open), credit::raw::Stored::new()),
102            flow: flow::Flow::new(build),
103        }
104    }
105
106    pub fn finished(done: admit::Track<admit::Finished>, build: B) -> Self {
107        let state = ingress::State::new(admit::Start::Finished(done), credit::raw::Stored::new());
108        let flow = flow::Flow::from_done(build, state.logical_len());
109        Self { state, flow }
110    }
111}
112
113impl<'d, B: body::Build<'d>, O: credit::Origin> Stream<'d, B, O> {
114    pub fn start(
115        open: admit::Track<admit::Open>,
116        build: B,
117        ledger: &credit::raw::Ledger<'_, O>,
118    ) -> Self {
119        Self {
120            state: ingress::State::new(admit::Start::Open(open), ledger.origin()),
121            flow: flow::Flow::new(build),
122        }
123    }
124
125    pub fn ended(
126        done: admit::Track<admit::Finished>,
127        build: B,
128        ledger: &credit::raw::Ledger<'_, O>,
129    ) -> Self {
130        let state = ingress::State::new(admit::Start::Finished(done), ledger.origin());
131        let flow = flow::Flow::from_done(build, state.logical_len());
132        Self { state, flow }
133    }
134
135    pub(super) const fn from_parts(state: ingress::State<O>, flow: flow::Flow<'d, B>) -> Self {
136        Self { state, flow }
137    }
138
139    pub const fn max_body(&self) -> usize {
140        self.state.max_body()
141    }
142
143    pub const fn retention(&self) -> admit::Retention {
144        self.state.retention()
145    }
146
147    pub fn discard<E: credit::Evidence>(
148        self,
149        ledger: &mut credit::raw::Ledger<'_, O>,
150        event: &mut E,
151    ) -> credit::Discard<O> {
152        self.into_release().try_discard(ledger, event)
153    }
154
155    pub fn abandon<E: credit::Evidence>(self, event: E) {
156        self.into_release().discard(event);
157    }
158
159    pub fn into_release(self) -> credit::Release<O> {
160        self.state.abandon()
161    }
162
163    pub fn try_discard<E>(
164        self,
165        ledger: &mut credit::raw::Ledger<'_, O>,
166        event: E,
167        _: owned::Segments<'d>,
168        bytes: usize,
169        boundary: Boundary,
170    ) -> body::Transfer<Self, body::Error>
171    where
172        E: credit::Evidence,
173    {
174        let Self { mut state, flow } = self;
175        let transfer = {
176            let mut turn = ingress::Turn::new(&mut state, ledger, event);
177            if turn.state().retention() != admit::Retention::Discarded {
178                body::Transfer::Rejected {
179                    build: flow,
180                    error: body::Error::SegmentMismatch,
181                }
182            } else {
183                match turn.try_advance(bytes) {
184                    Err(error) => body::Transfer::Rejected { build: flow, error },
185                    Ok(advance) => {
186                        advance.commit();
187                        match boundary {
188                            Boundary::More => body::Transfer::Accepted(flow),
189                            Boundary::End => flow.try_finish(turn.state_mut()),
190                        }
191                    }
192                }
193            }
194        };
195        transfer.map(|flow| Self { state, flow })
196    }
197
198    pub fn try_push<D, I, E, F>(
199        self,
200        ledger: &mut credit::raw::Ledger<'_, O>,
201        event: E,
202        input: input::Input<D>,
203        boundary: Boundary,
204        (rmax, cmax): (credit::Limits, credit::Limits),
205        parts: F,
206    ) -> Push<D, Self, <B as body::Push<'d>>::Error>
207    where
208        B: body::Push<'d>,
209        I: Parts<'d>,
210        E: credit::Evidence,
211        F: FnOnce(D) -> I,
212    {
213        let Self { mut state, flow } = self;
214        let (data, bytes, chunks) = input.into_parts();
215        let pushed = {
216            let mut turn = ingress::Turn::new(&mut state, ledger, event);
217            match turn.state().retention() {
218                admit::Retention::Discarded => match turn.try_advance(bytes) {
219                    Ok(advance) => {
220                        advance.commit();
221                        match boundary {
222                            Boundary::End => match flow.try_finish(turn.state_mut()) {
223                                body::Transfer::Accepted(flow) => Push::Discarded {
224                                    build: flow,
225                                    input: data,
226                                },
227                                body::Transfer::Rejected { build, error } => Push::Refused {
228                                    build,
229                                    input: data,
230                                    error,
231                                },
232                            },
233                            Boundary::More => Push::Discarded {
234                                build: flow,
235                                input: data,
236                            },
237                        }
238                    }
239                    Err(error) => Push::Refused {
240                        build: flow,
241                        input: data,
242                        error,
243                    },
244                },
245                admit::Retention::Retained => {
246                    let transfer = flow.try_push(&mut turn, parts(data), bytes, chunks, rmax, cmax);
247                    match transfer {
248                        flow::Push::Accepted(flow) => match boundary {
249                            Boundary::End => match flow.try_finish(turn.state_mut()) {
250                                body::Transfer::Accepted(flow) => Push::Accepted(flow),
251                                body::Transfer::Rejected { build, error } => {
252                                    Push::Failed { build, error }
253                                }
254                            },
255                            Boundary::More => Push::Accepted(flow),
256                        },
257                        flow::Push::Failed { build, error } => Push::Failed { build, error },
258                        flow::Push::Rejected { build, error } => Push::Rejected { build, error },
259                    }
260                }
261            }
262        };
263        pushed.map(|flow| Self { state, flow })
264    }
265
266    pub fn try_finish<E: credit::Evidence>(
267        self,
268        ledger: &mut credit::raw::Ledger<'_, O>,
269        event: E,
270    ) -> body::Transfer<Self, body::Error> {
271        let Self { mut state, flow } = self;
272        let transfer = {
273            let mut turn = ingress::Turn::new(&mut state, ledger, event);
274            flow.try_finish(turn.state_mut())
275        };
276        transfer.map(|flow| Self { state, flow })
277    }
278
279    /// Finishes with the carrier's final logical total, including prior input.
280    pub fn try_exact<E: credit::Evidence>(
281        self,
282        ledger: &mut credit::raw::Ledger<'_, O>,
283        event: E,
284        len: usize,
285    ) -> body::Transfer<Self, body::Error> {
286        let Self { mut state, flow } = self;
287        let transfer = {
288            let mut turn = ingress::Turn::new(&mut state, ledger, event);
289            flow.try_exact(&mut turn, len)
290        };
291        transfer.map(|flow| Self { state, flow })
292    }
293
294    pub fn try_drive<E: credit::Evidence>(
295        self,
296        ledger: &mut credit::raw::Ledger<'_, O>,
297        mut event: E,
298        budget: NonZero<usize>,
299    ) -> (Drive<'d, B, O>, E) {
300        let Self { state, flow } = self;
301        let step = flow.try_drive(state, ledger, &mut event, budget);
302        (step, event)
303    }
304
305    pub fn try_step<E: credit::Evidence>(
306        self,
307        ledger: &mut credit::raw::Ledger<'_, O>,
308        event: &mut E,
309        budget: NonZero<usize>,
310    ) -> Drive<'d, B, O> {
311        let (step, _) = self.try_drive(ledger, event, budget);
312        step
313    }
314}
315
316const _: () = assert!(
317    core::mem::size_of::<Push<usize, usize, u8>>()
318        <= core::mem::size_of::<Push<usize, usize, body::Error>>()
319);