ferrijs_std/stream_web/readable/stream/
algorithms.rs1use std::{cell::RefCell, rc::Rc};
2
3use crate::utils::option::{Null, Undefined};
4use crate::utils::primordials::Primordial;
5use rquickjs::{
6 class::Trace, prelude::This, Class, Ctx, Function, JsLifetime, Object, Promise, Result, Value,
7};
8
9use crate::stream_web::{
10 readable::controller::ReadableStreamControllerClass,
11 transform::{
12 controller::TransformStreamDefaultControllerClass,
13 stream::{self as transform_stream, TransformStreamClass},
14 },
15 utils::promise::{promise_resolved_with, PromisePrimordials},
16};
17
18use super::tee::TeeState;
19
20#[derive(Clone)]
21pub enum StartAlgorithm<'js> {
22 ReturnUndefined,
23 Function {
24 f: Function<'js>,
25 underlying_source: Null<Undefined<Object<'js>>>,
26 },
27}
28
29impl<'js> StartAlgorithm<'js> {
30 pub(crate) fn call(
31 &self,
32 ctx: Ctx<'js>,
33 controller: ReadableStreamControllerClass<'js>,
34 ) -> Result<Value<'js>> {
35 match self {
36 StartAlgorithm::ReturnUndefined => Ok(Value::new_undefined(ctx.clone())),
37 StartAlgorithm::Function {
38 f,
39 underlying_source,
40 } => f.call::<_, Value>((This(underlying_source.clone()), controller)),
41 }
42 }
43}
44
45type PullRustFn<'js> =
46 Box<dyn Fn(Ctx<'js>, ReadableStreamControllerClass<'js>) -> Result<Promise<'js>> + 'js>;
47
48#[allow(private_interfaces)]
49#[derive(Clone)]
50pub enum PullAlgorithm<'js> {
51 ReturnPromiseUndefined,
52 Function {
53 f: Function<'js>,
54 underlying_source: Null<Undefined<Object<'js>>>,
55 },
56 RustFunction(Rc<PullRustFn<'js>>),
57 Tee(Class<'js, TeeState<'js>>),
58 Transform(TransformStreamClass<'js>),
59}
60
61impl<'js> Trace<'js> for PullAlgorithm<'js> {
62 fn trace<'a>(&self, tracer: rquickjs::class::Tracer<'a, 'js>) {
63 match self {
64 Self::ReturnPromiseUndefined => {},
65 Self::Function {
66 f,
67 underlying_source,
68 } => {
69 f.trace(tracer);
70 underlying_source.trace(tracer);
71 },
72 Self::RustFunction(_) => {},
73 Self::Tee(state) => state.trace(tracer),
74 Self::Transform(stream) => stream.trace(tracer),
75 }
76 }
77}
78
79unsafe impl<'js> JsLifetime<'js> for PullAlgorithm<'js> {
80 type Changed<'to> = PullAlgorithm<'to>;
81}
82
83impl<'js> PullAlgorithm<'js> {
84 pub fn from_fn(
85 f: impl Fn(Ctx<'js>, ReadableStreamControllerClass<'js>) -> Result<Promise<'js>> + 'js,
86 ) -> Self {
87 Self::RustFunction(Rc::new(Box::new(f)))
88 }
89
90 pub fn from_fn_once(
94 f: impl FnOnce(Ctx<'js>, ReadableStreamControllerClass<'js>) -> Result<Promise<'js>> + 'js,
95 ) -> Self {
96 type OnceSlot<'js> = Rc<
97 RefCell<
98 Option<
99 Box<
100 dyn FnOnce(
101 Ctx<'js>,
102 ReadableStreamControllerClass<'js>,
103 ) -> Result<Promise<'js>>
104 + 'js,
105 >,
106 >,
107 >,
108 >;
109 let slot: OnceSlot<'js> = Rc::new(RefCell::new(Some(Box::new(f))));
110 Self::from_fn(move |ctx, ctrl| {
111 if let Some(f) = slot.borrow_mut().take() {
112 f(ctx, ctrl)
113 } else {
114 Ok(PromisePrimordials::get(&ctx)?
115 .promise_resolved_with_undefined
116 .clone())
117 }
118 })
119 }
120
121 pub(super) fn from_tee_state(state: Class<'js, TeeState<'js>>) -> Self {
122 Self::Tee(state)
123 }
124
125 pub(crate) fn call(
126 &self,
127 ctx: Ctx<'js>,
128 promise_primordials: &PromisePrimordials<'js>,
129 controller: ReadableStreamControllerClass<'js>,
130 ) -> Result<Promise<'js>> {
131 match self {
132 PullAlgorithm::ReturnPromiseUndefined => {
133 Ok(promise_primordials.promise_resolved_with_undefined.clone())
134 },
135 PullAlgorithm::Function {
136 f,
137 underlying_source,
138 } => promise_resolved_with(
139 &ctx,
140 promise_primordials,
141 f.call::<_, Value>((This(underlying_source.clone()), controller)),
142 ),
143 PullAlgorithm::RustFunction(f) => f(ctx, controller),
144 PullAlgorithm::Tee(state) => {
145 crate::stream_web::readable::stream::tee::tee_pull_algorithm(ctx, state.clone())
146 },
147 PullAlgorithm::Transform(stream) => {
148 transform_stream::source_pull_algorithm(ctx, stream)
149 },
150 }
151 }
152}
153
154type CancelRustFn<'js> = Box<dyn FnOnce(Value<'js>) -> Result<Promise<'js>> + 'js>;
155
156#[allow(private_interfaces)]
157pub enum CancelAlgorithm<'js> {
158 ReturnPromiseUndefined,
159 Function {
160 f: Function<'js>,
161 underlying_source: Null<Undefined<Object<'js>>>,
162 },
163 RustFunction(Rc<RefCell<Option<CancelRustFn<'js>>>>),
164 Tee1(Class<'js, TeeState<'js>>),
165 Tee2(Class<'js, TeeState<'js>>),
166 Transform {
167 stream: TransformStreamClass<'js>,
168 controller: TransformStreamDefaultControllerClass<'js>,
169 },
170}
171
172impl<'js> Clone for CancelAlgorithm<'js> {
173 fn clone(&self) -> Self {
174 match self {
175 Self::ReturnPromiseUndefined => Self::ReturnPromiseUndefined,
176 Self::Function {
177 f,
178 underlying_source,
179 } => Self::Function {
180 f: f.clone(),
181 underlying_source: underlying_source.clone(),
182 },
183 Self::RustFunction(rc) => Self::RustFunction(rc.clone()),
184 Self::Tee1(state) => Self::Tee1(state.clone()),
185 Self::Tee2(state) => Self::Tee2(state.clone()),
186 Self::Transform { stream, controller } => Self::Transform {
187 stream: stream.clone(),
188 controller: controller.clone(),
189 },
190 }
191 }
192}
193
194impl<'js> Trace<'js> for CancelAlgorithm<'js> {
195 fn trace<'a>(&self, tracer: rquickjs::class::Tracer<'a, 'js>) {
196 match self {
197 Self::ReturnPromiseUndefined => {},
198 Self::Function {
199 f,
200 underlying_source,
201 } => {
202 f.trace(tracer);
203 underlying_source.trace(tracer);
204 },
205 Self::RustFunction(_) => {},
206 Self::Tee1(state) | Self::Tee2(state) => state.trace(tracer),
207 Self::Transform { stream, controller } => {
208 stream.trace(tracer);
209 controller.trace(tracer);
210 },
211 }
212 }
213}
214
215unsafe impl<'js> JsLifetime<'js> for CancelAlgorithm<'js> {
216 type Changed<'to> = CancelAlgorithm<'to>;
217}
218
219impl<'js> CancelAlgorithm<'js> {
220 pub fn from_fn(f: impl FnOnce(Value<'js>) -> Result<Promise<'js>> + 'js) -> Self {
221 Self::RustFunction(Rc::new(RefCell::new(Some(Box::new(f)))))
222 }
223
224 pub(super) fn from_tee_state_1(state: Class<'js, TeeState<'js>>) -> Self {
225 Self::Tee1(state)
226 }
227
228 pub(super) fn from_tee_state_2(state: Class<'js, TeeState<'js>>) -> Self {
229 Self::Tee2(state)
230 }
231
232 pub(crate) fn call(
233 &self,
234 ctx: Ctx<'js>,
235 promise_primordials: &PromisePrimordials<'js>,
236 reason: Value<'js>,
237 ) -> Result<Promise<'js>> {
238 match self {
239 CancelAlgorithm::ReturnPromiseUndefined => {
240 Ok(promise_primordials.promise_resolved_with_undefined.clone())
241 },
242 CancelAlgorithm::Function {
243 f,
244 underlying_source,
245 } => {
246 let result: Result<Value> = f.call((This(underlying_source.clone()), reason));
247 promise_resolved_with(&ctx, promise_primordials, result)
248 },
249 CancelAlgorithm::RustFunction(f) => {
250 let f = f
251 .borrow_mut()
252 .take()
253 .expect("cancel algorithm must only be called once");
254 f(reason)
255 },
256 CancelAlgorithm::Tee1(state) => {
257 crate::stream_web::readable::stream::tee::tee_cancel_algorithm(ctx, state.clone(), reason, 0)
258 },
259 CancelAlgorithm::Tee2(state) => {
260 crate::stream_web::readable::stream::tee::tee_cancel_algorithm(ctx, state.clone(), reason, 1)
261 },
262 CancelAlgorithm::Transform { stream, controller } => {
263 transform_stream::source_cancel_algorithm(ctx, stream, controller, reason)
264 },
265 }
266 }
267}