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
13pub 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#[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 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#[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 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);