Skip to main content

spacetimedb/
rt.rs

1#![deny(unsafe_op_in_unsafe_fn)]
2
3use crate::query_builder::Query;
4use crate::table::IndexAlgo;
5use crate::{sys, AnonymousViewContext, IterBuf, ReducerContext, ReducerResult, SpacetimeType, Table, ViewContext};
6use spacetimedb_lib::bsatn::EncodeError;
7pub use spacetimedb_lib::db::raw_def::v9::Lifecycle as LifecycleReducer;
8use spacetimedb_lib::db::raw_def::v9::{RawIndexAlgorithm, RawModuleDefV9Builder, TableType, ViewResultHeader};
9use spacetimedb_lib::de::{self, Deserialize, DeserializeOwned, Error as _, SeqProductAccess};
10use spacetimedb_lib::sats::typespace::TypespaceBuilder;
11use spacetimedb_lib::sats::{impl_deserialize, impl_serialize, ProductTypeElement};
12use spacetimedb_lib::ser::{Serialize, SerializeSeqProduct};
13use spacetimedb_lib::{bsatn, AlgebraicType, ConnectionId, Identity, ProductType, RawModuleDef, Timestamp};
14use spacetimedb_primitives::*;
15use std::convert::Infallible;
16use std::fmt;
17use std::marker::PhantomData;
18use std::sync::{Mutex, OnceLock};
19pub use sys::raw::{BytesSink, BytesSource};
20
21#[cfg(feature = "unstable")]
22use crate::{ProcedureContext, ProcedureResult};
23
24pub trait IntoVec<T> {
25    fn into_vec(self) -> Vec<T>;
26}
27
28impl<T> IntoVec<T> for Vec<T> {
29    fn into_vec(self) -> Vec<T> {
30        self
31    }
32}
33
34impl<T> IntoVec<T> for Option<T> {
35    fn into_vec(self) -> Vec<T> {
36        self.into_iter().collect()
37    }
38}
39
40/// The `sender` invokes `reducer` at `timestamp` and provides it with the given `args`.
41///
42/// Returns an invalid buffer on success
43/// and otherwise the error is written into the fresh one returned.
44pub fn invoke_reducer<'a, A: Args<'a>>(
45    reducer: impl Reducer<'a, A>,
46    ctx: ReducerContext,
47    args: &'a [u8],
48) -> Result<(), Box<str>> {
49    // Deserialize the arguments from a bsatn encoding.
50    let SerDeArgs(args) = bsatn::from_slice(args).expect("unable to decode args");
51
52    reducer.invoke(&ctx, args)
53}
54
55#[cfg(feature = "unstable")]
56pub fn invoke_procedure<'a, A: Args<'a>, Ret: IntoProcedureResult>(
57    procedure: impl Procedure<'a, A, Ret>,
58    mut ctx: ProcedureContext,
59    args: &'a [u8],
60) -> ProcedureResult {
61    // Deserialize the arguments from a bsatn encoding.
62    let SerDeArgs(args) = bsatn::from_slice(args).expect("unable to decode args");
63
64    let res = procedure.invoke(&mut ctx, args);
65
66    res.to_result()
67}
68
69/// A trait for types representing the *execution logic* of a reducer.
70#[diagnostic::on_unimplemented(
71    message = "invalid reducer signature",
72    label = "this reducer signature is not valid",
73    note = "",
74    note = "reducer signatures must match the following pattern:",
75    note = "    `Fn(&ReducerContext, [T1, ...]) [-> Result<(), impl Display>]`",
76    note = "where each `Ti` type implements `SpacetimeType`.",
77    note = ""
78)]
79pub trait Reducer<'de, A: Args<'de>> {
80    fn invoke(&self, ctx: &ReducerContext, args: A) -> ReducerResult;
81}
82
83/// Invoke a caller-specific view.
84/// Returns a BSATN encoded `Vec` of rows.
85pub fn invoke_view<'a, A: Args<'a>, T: ViewReturn>(
86    view: impl View<'a, A, T>,
87    ctx: ViewContext,
88    args: &'a [u8],
89) -> Vec<u8> {
90    // Deserialize the arguments from a bsatn encoding.
91    let SerDeArgs(args) = bsatn::from_slice(args).expect("unable to decode args");
92    let retn = view.invoke(&ctx, args);
93    let mut buf = IterBuf::take();
94    retn.to_writer(&mut buf).expect("unable to encode view return value");
95    std::mem::take(&mut *buf)
96}
97/// A trait for types representing the execution logic of a caller-specific view.
98#[diagnostic::on_unimplemented(
99    message = "invalid view signature",
100    label = "this view signature is not valid",
101    note = "",
102    note = "view signatures must match:",
103    note = "    `Fn(&ViewContext, [T1, ...]) -> Vec<Tn> | Option<Tn>`",
104    note = "where each `Ti` implements `SpacetimeType`.",
105    note = ""
106)]
107pub trait View<'de, A: Args<'de>, T: ViewReturn> {
108    fn invoke(&self, ctx: &ViewContext, args: A) -> T;
109}
110
111/// Invoke an anonymous view.
112/// Returns a BSATN encoded `Vec` of rows.
113pub fn invoke_anonymous_view<'a, A: Args<'a>, T: ViewReturn>(
114    view: impl AnonymousView<'a, A, T>,
115    ctx: AnonymousViewContext,
116    args: &'a [u8],
117) -> Vec<u8> {
118    // Deserialize the arguments from a bsatn encoding.
119    let SerDeArgs(args) = bsatn::from_slice(args).expect("unable to decode args");
120    let retn = view.invoke(&ctx, args);
121    let mut buf = IterBuf::take();
122    retn.to_writer(&mut buf).expect("unable to encode view return value");
123    std::mem::take(&mut *buf)
124}
125/// A trait for types representing the execution logic of an anonymous view.
126#[diagnostic::on_unimplemented(
127    message = "invalid anonymous view signature",
128    label = "this view signature is not valid",
129    note = "",
130    note = "anonymous view signatures must match:",
131    note = "    `Fn(&AnonymousViewContext, [T1, ...]) -> Vec<Tn> | Option<Tn>`",
132    note = "where each `Ti` implements `SpacetimeType`.",
133    note = ""
134)]
135pub trait AnonymousView<'de, A: Args<'de>, T: ViewReturn> {
136    fn invoke(&self, ctx: &AnonymousViewContext, args: A) -> T;
137}
138
139/// A trait for types that can *describe* a callable function such as a reducer or view.
140pub trait FnInfo {
141    /// The type of function to invoke.
142    type Invoke;
143
144    #[cfg_attr(
145        feature = "unstable",
146        doc = "One of [`FnKindReducer`], [`FnKindProcedure`] or [`FnKindView`]."
147    )]
148    #[cfg_attr(not(feature = "unstable"), doc = "Either [`FnKindReducer`] or [`FnKindView`].")]
149    ///
150    /// Used as a type argument to [`ExportFunctionForScheduledTable`] and [`scheduled_typecheck`].
151    /// See <https://willcrichton.net/notes/defeating-coherence-rust/> for details on this technique.
152    type FnKind;
153
154    /// The name of the function.
155    const NAME: &'static str;
156
157    /// The lifecycle of the function, if there is one.
158    const LIFECYCLE: Option<LifecycleReducer> = None;
159
160    /// A description of the parameter names of the function.
161    const ARG_NAMES: &'static [Option<&'static str>];
162
163    /// The function to invoke.
164    const INVOKE: Self::Invoke;
165
166    /// The return type of this function.
167    /// Currently only implemented for views.
168    fn return_type(_ts: &mut impl TypespaceBuilder) -> Option<AlgebraicType> {
169        None
170    }
171}
172
173#[cfg(feature = "unstable")]
174pub trait Procedure<'de, A: Args<'de>, Ret: IntoProcedureResult> {
175    fn invoke(&self, ctx: &mut ProcedureContext, args: A) -> Ret;
176}
177
178/// A trait of types representing the arguments of a reducer, procedure or view.
179///
180/// This does not include the context first argument,
181/// only the client-provided args.
182/// As such, the same trait can be used for all sorts of exported functions.
183pub trait Args<'de>: Sized {
184    /// How many arguments does the reducer accept?
185    const LEN: usize;
186
187    /// Deserialize the arguments from the sequence `prod` which knows when there are next elements.
188    fn visit_seq_product<A: SeqProductAccess<'de>>(prod: A) -> Result<Self, A::Error>;
189
190    /// Serialize the arguments in `self` into the sequence `prod` according to the type `S`.
191    fn serialize_seq_product<S: SerializeSeqProduct>(&self, prod: &mut S) -> Result<(), S::Error>;
192
193    /// Returns the schema of the args for this function provided a `typespace`.
194    fn schema<I: FnInfo>(typespace: &mut impl TypespaceBuilder) -> ProductType;
195}
196
197/// A trait of types representing the result of executing a reducer.
198#[diagnostic::on_unimplemented(
199    message = "`{Self}` is not a valid reducer return type",
200    note = "reducers cannot return values -- you can only return `()` or `Result<(), impl Display>`"
201)]
202pub trait IntoReducerResult {
203    /// Convert the result into form where there is no value
204    /// and the error message is a string.
205    fn into_result(self) -> Result<(), Box<str>>;
206}
207impl IntoReducerResult for () {
208    #[inline]
209    fn into_result(self) -> Result<(), Box<str>> {
210        Ok(self)
211    }
212}
213impl<E: fmt::Display> IntoReducerResult for Result<(), E> {
214    #[inline]
215    fn into_result(self) -> Result<(), Box<str>> {
216        self.map_err(|e| e.to_string().into())
217    }
218}
219
220#[cfg(feature = "unstable")]
221#[diagnostic::on_unimplemented(
222    message = "The procedure return type `{Self}` does not implement `SpacetimeType`",
223    note = "if you own the type, try adding `#[derive(SpacetimeType)]` to its definition"
224)]
225pub trait IntoProcedureResult: SpacetimeType + Serialize {
226    #[inline]
227    fn to_result(&self) -> ProcedureResult {
228        bsatn::to_vec(&self).expect("Failed to serialize procedure result")
229    }
230}
231#[cfg(feature = "unstable")]
232impl<T: SpacetimeType + Serialize> IntoProcedureResult for T {}
233
234#[diagnostic::on_unimplemented(
235    message = "the first argument of a reducer must be `&ReducerContext`",
236    label = "first argument must be `&ReducerContext`"
237)]
238pub trait ReducerContextArg {
239    // a little hack used in the macro to make error messages nicer. it generates <T as ReducerContextArg>::_ITEM
240    #[doc(hidden)]
241    const _ITEM: () = ();
242}
243impl ReducerContextArg for &ReducerContext {}
244
245/// A trait of types that can be an argument of a reducer.
246#[diagnostic::on_unimplemented(
247    message = "the reducer argument `{Self}` does not implement `SpacetimeType`",
248    note = "if you own the type, try adding `#[derive(SpacetimeType)]` to its definition"
249)]
250pub trait ReducerArg {
251    // a little hack used in the macro to make error messages nicer. it generates <T as ReducerArg>::_ITEM
252    #[doc(hidden)]
253    const _ITEM: () = ();
254}
255impl<T: SpacetimeType> ReducerArg for T {}
256
257#[cfg(feature = "unstable")]
258#[diagnostic::on_unimplemented(
259    message = "the first argument of a procedure must be `&mut ProcedureContext`",
260    label = "first argument must be `&mut ProcedureContext`"
261)]
262pub trait ProcedureContextArg {
263    // a little hack used in the macro to make error messages nicer. it generates <T as ReducerContextArg>::_ITEM
264    #[doc(hidden)]
265    const _ITEM: () = ();
266}
267#[cfg(feature = "unstable")]
268impl ProcedureContextArg for &mut ProcedureContext {}
269
270/// A trait of types that can be an argument of a procedure.
271#[cfg(feature = "unstable")]
272#[diagnostic::on_unimplemented(
273    message = "the procedure argument `{Self}` does not implement `SpacetimeType`",
274    note = "if you own the type, try adding `#[derive(SpacetimeType)]` to its definition"
275)]
276pub trait ProcedureArg {
277    // a little hack used in the macro to make error messages nicer. it generates <T as ReducerArg>::_ITEM
278    #[doc(hidden)]
279    const _ITEM: () = ();
280}
281#[cfg(feature = "unstable")]
282impl<T: SpacetimeType> ProcedureArg for T {}
283
284#[diagnostic::on_unimplemented(
285    message = "The first parameter of a `#[view]` must be `&ViewContext` or `&AnonymousViewContext`"
286)]
287pub trait ViewContextArg {
288    #[doc(hidden)]
289    const _ITEM: () = ();
290}
291impl ViewContextArg for ViewContext {}
292impl ViewContextArg for AnonymousViewContext {}
293
294/// A trait of types that can be an argument of a view.
295#[diagnostic::on_unimplemented(
296    message = "the view argument `{Self}` does not implement `SpacetimeType`",
297    note = "if you own the type, try adding `#[derive(SpacetimeType)]` to its definition"
298)]
299pub trait ViewArg {
300    #[doc(hidden)]
301    const _ITEM: () = ();
302}
303impl<T: SpacetimeType> ViewArg for T {}
304
305/// A trait of types that can be the return type of a view.
306#[diagnostic::on_unimplemented(message = "Views must return `Vec<T>` or `Option<T>` where `T` is a `SpacetimeType`")]
307pub trait ViewReturn {
308    #[doc(hidden)]
309    const _ITEM: () = ();
310
311    fn to_writer(self, w: &mut Vec<u8>) -> Result<(), EncodeError>;
312}
313
314impl<T: SpacetimeType + Serialize> ViewReturn for Vec<T> {
315    fn to_writer(self, buf: &mut Vec<u8>) -> Result<(), EncodeError> {
316        bsatn::to_writer(buf, &ViewResultHeader::RowData)?;
317        bsatn::to_writer(buf, &self)
318    }
319}
320
321impl<T: SpacetimeType + Serialize> ViewReturn for Option<T> {
322    fn to_writer(self, buf: &mut Vec<u8>) -> Result<(), EncodeError> {
323        bsatn::to_writer(buf, &ViewResultHeader::RowData)?;
324        bsatn::to_writer(buf, self.as_slice())
325    }
326}
327
328impl<T: SpacetimeType + Serialize> ViewReturn for Query<T> {
329    fn to_writer(self, buf: &mut Vec<u8>) -> Result<(), EncodeError> {
330        bsatn::to_writer(buf, &ViewResultHeader::RawSql(self.sql().to_string()))
331    }
332}
333
334/// Map the correct dispatcher based on the `Ctx` type
335pub struct ViewKind<Ctx> {
336    _marker: PhantomData<Ctx>,
337}
338
339pub trait ViewKindTrait {
340    type InvokeFn;
341}
342
343impl ViewKindTrait for ViewKind<ViewContext> {
344    type InvokeFn = ViewFn;
345}
346
347impl ViewKindTrait for ViewKind<AnonymousViewContext> {
348    type InvokeFn = AnonymousFn;
349}
350
351/// Invoke the correct dispatcher based on the `Ctx` type
352pub struct ViewDispatcher<Ctx> {
353    _marker: PhantomData<Ctx>,
354}
355
356impl ViewDispatcher<ViewContext> {
357    #[inline]
358    pub fn invoke<'a, A, T, V>(view: V, ctx: ViewContext, args: &'a [u8]) -> Vec<u8>
359    where
360        A: Args<'a>,
361        T: ViewReturn,
362        V: View<'a, A, T>,
363    {
364        invoke_view(view, ctx, args)
365    }
366}
367
368impl ViewDispatcher<AnonymousViewContext> {
369    #[inline]
370    pub fn invoke<'a, A, T, V>(view: V, ctx: AnonymousViewContext, args: &'a [u8]) -> Vec<u8>
371    where
372        A: Args<'a>,
373        T: ViewReturn,
374        V: AnonymousView<'a, A, T>,
375    {
376        invoke_anonymous_view(view, ctx, args)
377    }
378}
379
380/// Register the correct dispatcher based on the `Ctx` type
381pub struct ViewRegistrar<Ctx> {
382    _marker: PhantomData<Ctx>,
383}
384
385impl ViewRegistrar<ViewContext> {
386    #[inline]
387    pub fn register<'a, A, I, T, V>(view: V)
388    where
389        A: Args<'a>,
390        T: ViewReturn,
391        I: FnInfo<Invoke = ViewFn>,
392        V: View<'a, A, T>,
393    {
394        register_view::<A, I, T>(view)
395    }
396}
397
398impl ViewRegistrar<AnonymousViewContext> {
399    #[inline]
400    pub fn register<'a, A, I, T, V>(view: V)
401    where
402        A: Args<'a>,
403        T: ViewReturn,
404        I: FnInfo<Invoke = AnonymousFn>,
405        V: AnonymousView<'a, A, T>,
406    {
407        register_anonymous_view::<A, I, T>(view)
408    }
409}
410
411/// Assert that a reducer type-checks with a given type.
412pub const fn scheduled_typecheck<'de, Row, FnKind>(_x: impl ExportFunctionForScheduledTable<'de, Row, FnKind>)
413where
414    Row: SpacetimeType + Serialize + Deserialize<'de>,
415{
416    core::mem::forget(_x);
417}
418
419/// Tacit marker argument to [`ExportFunctionForScheduledTable`] for reducers.
420pub struct FnKindReducer {
421    _never: Infallible,
422}
423
424#[cfg(feature = "unstable")]
425/// Tacit marker argument to [`ExportFunctionForScheduledTable`] for procedures.
426///
427/// Holds the procedure's return type in order to avoid an error due to an unconstrained type argument.
428pub struct FnKindProcedure<Ret> {
429    _never: Infallible,
430    _ret_ty: PhantomData<fn() -> Ret>,
431}
432
433/// Tacit marker argument to [`ExportFunctionForScheduledTable`] for views.
434///
435/// Because views are never scheduled, we don't need to distinguish between anonymous or sender-identity views,
436/// or to include their return type.
437pub struct FnKindView {
438    _never: Infallible,
439}
440
441/// Trait bound for [`scheduled_typecheck`], which the [`crate::table`] macro generates to typecheck scheduled functions.
442///
443/// The `FnKind` parameter here is a coherence-defeating marker, which Will Crichton calls a "tacit parameter."
444/// See <https://willcrichton.net/notes/defeating-coherence-rust/> for details on this technique.
445#[cfg_attr(
446    feature = "unstable",
447    doc = "It will be one of [`FnKindReducer`] or [`FnKindProcedure`] in modules that compile successfully."
448)]
449#[cfg_attr(
450    not(feature = "unstable"),
451    doc = "It will be [`FnKindReducer`] in modules that compile successfully."
452)]
453///
454/// It may be [`FnKindView`], but that will always fail to typecheck, as views cannot be used as scheduled functions.
455#[diagnostic::on_unimplemented(
456    message = "invalid signature for scheduled table reducer or procedure",
457    note = "views cannot be scheduled",
458    note = "the scheduled function must take `{TableRow}` as its sole argument",
459    note = "e.g: `fn scheduled_reducer(ctx: &ReducerContext, arg: {TableRow})`",
460    // note = "or `fn scheduled_procedure(ctx: &mut ProcedureContext, arg: {TableRow})`"
461)]
462pub trait ExportFunctionForScheduledTable<'de, TableRow, FnKind> {}
463impl<'de, TableRow: SpacetimeType + Serialize + Deserialize<'de>, F: Reducer<'de, (TableRow,)>>
464    ExportFunctionForScheduledTable<'de, TableRow, FnKindReducer> for F
465{
466}
467
468#[cfg(feature = "unstable")]
469impl<
470        'de,
471        TableRow: SpacetimeType + Serialize + Deserialize<'de>,
472        Ret: SpacetimeType + Serialize + Deserialize<'de>,
473        F: Procedure<'de, (TableRow,), Ret>,
474    > ExportFunctionForScheduledTable<'de, TableRow, FnKindProcedure<Ret>> for F
475{
476}
477
478// the macro generates <T as SpacetimeType>::make_type::<DummyTypespace>
479pub struct DummyTypespace;
480impl TypespaceBuilder for DummyTypespace {
481    fn add(
482        &mut self,
483        _: std::any::TypeId,
484        _: Option<&'static str>,
485        _: impl FnOnce(&mut Self) -> spacetimedb_lib::AlgebraicType,
486    ) -> spacetimedb_lib::AlgebraicType {
487        unreachable!()
488    }
489}
490
491#[diagnostic::on_unimplemented(
492    message = "the column type `{Self}` does not implement `SpacetimeType`",
493    note = "table column types all must implement `SpacetimeType`",
494    note = "if you own the type, try adding `#[derive(SpacetimeType)]` to its definition"
495)]
496pub trait TableColumn {
497    // a little hack used in the macro to make error messages nicer. it generates <T as TableColumn>::_ITEM
498    #[doc(hidden)]
499    const _ITEM: () = ();
500}
501impl<T: SpacetimeType> TableColumn for T {}
502
503/// Assert that the primary_key column of a scheduled table is a u64.
504pub const fn assert_scheduled_table_primary_key<T: ScheduledTablePrimaryKey>() {}
505
506mod sealed {
507    pub trait Sealed {}
508}
509#[diagnostic::on_unimplemented(
510    message = "scheduled table primary key must be a `u64`",
511    label = "should be `u64`, not `{Self}`"
512)]
513pub trait ScheduledTablePrimaryKey: sealed::Sealed {}
514impl sealed::Sealed for u64 {}
515impl ScheduledTablePrimaryKey for u64 {}
516
517/// Used in the last type parameter of `Reducer` to indicate that the
518/// context argument *should* be passed to the reducer logic.
519pub struct ContextArg;
520
521/// A visitor providing a deserializer for a type `A: Args`.
522struct ArgsVisitor<A> {
523    _marker: PhantomData<A>,
524}
525
526impl<'de, A: Args<'de>> de::ProductVisitor<'de> for ArgsVisitor<A> {
527    type Output = A;
528
529    fn product_name(&self) -> Option<&str> {
530        None
531    }
532    fn product_len(&self) -> usize {
533        A::LEN
534    }
535    fn product_kind(&self) -> de::ProductKind {
536        de::ProductKind::ReducerArgs
537    }
538    fn visit_seq_product<Acc: SeqProductAccess<'de>>(self, prod: Acc) -> Result<Self::Output, Acc::Error> {
539        A::visit_seq_product(prod)
540    }
541    fn visit_named_product<Acc: de::NamedProductAccess<'de>>(self, _prod: Acc) -> Result<Self::Output, Acc::Error> {
542        Err(Acc::Error::named_products_not_supported())
543    }
544}
545
546macro_rules! impl_reducer_procedure_view {
547    ($($T1:ident $(, $T:ident)*)?) => {
548        impl_reducer_procedure_view!(@impl $($T1 $(, $T)*)?);
549        $(impl_reducer_procedure_view!($($T),*);)?
550    };
551    (@impl $($T:ident),*) => {
552        // Implement `Args` for the tuple type `($($T,)*)`.
553        impl<'de, $($T: SpacetimeType + Deserialize<'de> + Serialize),*> Args<'de> for ($($T,)*) {
554            const LEN: usize = impl_reducer_procedure_view!(@count $($T)*);
555            #[allow(non_snake_case)]
556            #[allow(unused)]
557            fn visit_seq_product<Acc: SeqProductAccess<'de>>(mut prod: Acc) -> Result<Self, Acc::Error> {
558                let vis = ArgsVisitor { _marker: PhantomData::<Self> };
559                // Counts the field number; only relevant for errors.
560                let i = 0;
561                // For every element in the product, deserialize.
562                $(
563                    let $T = prod.next_element::<$T>()?.ok_or_else(|| de::Error::missing_field(i, None, &vis))?;
564                    let i = i + 1;
565                )*
566                Ok(($($T,)*))
567            }
568
569            #[allow(non_snake_case)]
570            fn serialize_seq_product<Ser: SerializeSeqProduct>(&self, _prod: &mut Ser) -> Result<(), Ser::Error> {
571                // For every element in the product, serialize.
572                let ($($T,)*) = self;
573                $(_prod.serialize_element($T)?;)*
574                Ok(())
575            }
576
577            #[inline]
578            #[allow(non_snake_case, irrefutable_let_patterns)]
579            fn schema<Info: FnInfo>(_typespace: &mut impl TypespaceBuilder) -> ProductType {
580                // Extract the names of the arguments.
581                let [.., $($T),*] = Info::ARG_NAMES else { panic!() };
582                ProductType::new(vec![
583                        $(ProductTypeElement {
584                            name: $T.map(Into::into),
585                            algebraic_type: <$T>::make_type(_typespace),
586                        }),*
587                ].into())
588            }
589        }
590
591                // Implement `Reducer<..., ContextArg>` for the tuple type `($($T,)*)`.
592        impl<'de, Func, Ret, $($T: SpacetimeType + Deserialize<'de> + Serialize),*> Reducer<'de, ($($T,)*)> for Func
593        where
594            Func: Fn(&ReducerContext, $($T),*) -> Ret,
595            Ret: IntoReducerResult
596        {
597            #[allow(non_snake_case)]
598            fn invoke(&self, ctx: &ReducerContext, args: ($($T,)*)) -> Result<(), Box<str>> {
599                let ($($T,)*) = args;
600                self(ctx, $($T),*).into_result()
601            }
602        }
603
604        #[cfg(feature = "unstable")]
605        impl<'de, Func, Ret, $($T: SpacetimeType + Deserialize<'de> + Serialize),*> Procedure<'de, ($($T,)*), Ret> for Func
606        where
607            Func: Fn(&mut ProcedureContext, $($T),*) -> Ret,
608            Ret: IntoProcedureResult,
609        {
610            #[allow(non_snake_case)]
611            fn invoke(&self, ctx: &mut ProcedureContext, args: ($($T,)*)) -> Ret {
612                let ($($T,)*) = args;
613                self(ctx, $($T),*)
614            }
615        }
616
617        // Implement `View<..., ViewContext>` for the tuple type `($($T,)*)`.
618        impl<'de, Func, Retn, $($T),*>
619            View<'de, ($($T,)*), Retn> for Func
620        where
621            $($T: SpacetimeType + Deserialize<'de> + Serialize,)*
622            Func: Fn(&ViewContext, $($T),*) -> Retn,
623            Retn: ViewReturn,
624        {
625            #[allow(non_snake_case)]
626            fn invoke(&self, ctx: &ViewContext, args: ($($T,)*)) -> Retn {
627                let ($($T,)*) = args;
628                self(ctx, $($T),*)
629            }
630        }
631
632        // Implement `View<..., AnonymousViewContext>` for the tuple type `($($T,)*)`.
633        impl<'de, Func, Retn, $($T),*>
634            AnonymousView<'de, ($($T,)*), Retn> for Func
635        where
636            $($T: SpacetimeType + Deserialize<'de> + Serialize,)*
637            Func: Fn(&AnonymousViewContext, $($T),*) -> Retn,
638            Retn: ViewReturn,
639        {
640            #[allow(non_snake_case)]
641            fn invoke(&self, ctx: &AnonymousViewContext, args: ($($T,)*)) -> Retn {
642                let ($($T,)*) = args;
643                self(ctx, $($T),*)
644            }
645        }
646    };
647    // Counts the number of elements in the tuple.
648    (@count $($T:ident)*) => {
649        0 $(+ impl_reducer_procedure_view!(@drop $T 1))*
650    };
651    (@drop $a:tt $b:tt) => { $b };
652}
653
654impl_reducer_procedure_view!(
655    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA, AB, AC, AD, AE, AF
656);
657
658/// Provides deserialization and serialization for any type `A: Args`.
659struct SerDeArgs<A>(A);
660impl_deserialize!(
661    [A: Args<'de>] SerDeArgs<A>,
662    de => de.deserialize_product(ArgsVisitor { _marker: PhantomData }).map(Self)
663);
664impl_serialize!(['de, A: Args<'de>] SerDeArgs<A>, (self, ser) => {
665    let mut prod = ser.serialize_seq_product(A::LEN)?;
666    self.0.serialize_seq_product(&mut prod)?;
667    prod.end()
668});
669
670/// A trait for types that can *describe* a row-level security policy.
671pub trait RowLevelSecurityInfo {
672    /// The SQL expression for the row-level security policy.
673    const SQL: &'static str;
674}
675
676/// A function which will be registered by [`register_describer`] into [`DESCRIBERS`],
677/// which will be called by [`__describe_module__`] to construct a module definition.
678///
679/// May be a closure over static data, so that e.g.
680/// [`register_row_level_security`] doesn't need to take a type parameter.
681/// Permitted by the type system to be a [`FnMut`] mutable closure,
682/// since [`DESCRIBERS`] is in a [`Mutex`] anyways,
683/// but will likely cause weird misbehaviors if a non-idempotent function is used.
684trait DescriberFn: FnMut(&mut ModuleBuilder) + Send + 'static {}
685impl<F: FnMut(&mut ModuleBuilder) + Send + 'static> DescriberFn for F {}
686
687/// Registers into `DESCRIBERS` a function `f` to modify the module builder.
688fn register_describer(f: impl DescriberFn) {
689    DESCRIBERS.lock().unwrap().push(Box::new(f))
690}
691
692/// Registers a describer for the `SpacetimeType` `T`.
693pub fn register_reftype<T: SpacetimeType>() {
694    register_describer(|module| {
695        T::make_type(&mut module.inner);
696    })
697}
698
699/// Registers a describer for the `TableType` `T`.
700pub fn register_table<T: Table>() {
701    register_describer(|module| {
702        let product_type_ref = *T::Row::make_type(&mut module.inner).as_ref().unwrap();
703
704        let mut table = module
705            .inner
706            .build_table(T::TABLE_NAME, product_type_ref)
707            .with_type(TableType::User)
708            .with_access(T::TABLE_ACCESS);
709
710        for &col in T::UNIQUE_COLUMNS {
711            table = table.with_unique_constraint(col);
712        }
713        for &index in T::INDEXES {
714            table = table.with_index(index.algo.into(), index.accessor_name);
715        }
716        if let Some(primary_key) = T::PRIMARY_KEY {
717            table = table.with_primary_key(primary_key);
718        }
719        for &col in T::SEQUENCES {
720            table = table.with_column_sequence(col);
721        }
722        if let Some(schedule) = T::SCHEDULE {
723            table = table.with_schedule(schedule.reducer_or_procedure_name, schedule.scheduled_at_column);
724        }
725
726        for col in T::get_default_col_values().iter_mut() {
727            table = table.with_default_column_value(col.col_id, col.value.clone())
728        }
729
730        table.finish();
731    })
732}
733
734impl From<IndexAlgo<'_>> for RawIndexAlgorithm {
735    fn from(algo: IndexAlgo<'_>) -> RawIndexAlgorithm {
736        match algo {
737            IndexAlgo::BTree { columns } => RawIndexAlgorithm::BTree {
738                columns: columns.iter().copied().collect(),
739            },
740            IndexAlgo::Hash { columns } => RawIndexAlgorithm::Hash {
741                columns: columns.iter().copied().collect(),
742            },
743            IndexAlgo::Direct { column } => RawIndexAlgorithm::Direct { column: column.into() },
744        }
745    }
746}
747
748/// Registers a describer for the reducer `I` with arguments `A`.
749pub fn register_reducer<'a, A: Args<'a>, I: FnInfo<Invoke = ReducerFn>>(_: impl Reducer<'a, A>) {
750    register_describer(|module| {
751        let params = A::schema::<I>(&mut module.inner);
752        module.inner.add_reducer(I::NAME, params, I::LIFECYCLE);
753        module.reducers.push(I::INVOKE);
754    })
755}
756
757#[cfg(feature = "unstable")]
758pub fn register_procedure<'a, A, Ret, I>(_: impl Procedure<'a, A, Ret>)
759where
760    A: Args<'a>,
761    Ret: SpacetimeType + Serialize,
762    I: FnInfo<Invoke = ProcedureFn>,
763{
764    register_describer(|module| {
765        let params = A::schema::<I>(&mut module.inner);
766        let ret_ty = <Ret as SpacetimeType>::make_type(&mut module.inner);
767        module.inner.add_procedure(I::NAME, params, ret_ty);
768        module.procedures.push(I::INVOKE);
769    })
770}
771
772/// Registers a describer for the view `I` with arguments `A` and return type `Vec<T>`.
773pub fn register_view<'a, A, I, T>(_: impl View<'a, A, T>)
774where
775    A: Args<'a>,
776    I: FnInfo<Invoke = ViewFn>,
777    T: ViewReturn,
778{
779    register_describer(|module| {
780        let params = A::schema::<I>(&mut module.inner);
781        let return_type = I::return_type(&mut module.inner).unwrap();
782        module
783            .inner
784            .add_view(I::NAME, module.views.len(), true, false, params, return_type);
785        module.views.push(I::INVOKE);
786    })
787}
788
789/// Registers a describer for the anonymous view `I` with arguments `A` and return type `Vec<T>`.
790pub fn register_anonymous_view<'a, A, I, T>(_: impl AnonymousView<'a, A, T>)
791where
792    A: Args<'a>,
793    I: FnInfo<Invoke = AnonymousFn>,
794    T: ViewReturn,
795{
796    register_describer(|module| {
797        let params = A::schema::<I>(&mut module.inner);
798        let return_type = I::return_type(&mut module.inner).unwrap();
799        module
800            .inner
801            .add_view(I::NAME, module.views_anon.len(), true, true, params, return_type);
802        module.views_anon.push(I::INVOKE);
803    })
804}
805
806/// Registers a row-level security policy.
807pub fn register_row_level_security(sql: &'static str) {
808    register_describer(|module| {
809        module.inner.add_row_level_security(sql);
810    })
811}
812
813/// A builder for a module.
814#[derive(Default)]
815pub struct ModuleBuilder {
816    /// The module definition.
817    inner: RawModuleDefV9Builder,
818    /// The reducers of the module.
819    reducers: Vec<ReducerFn>,
820    /// The procedures of the module.
821    #[cfg(feature = "unstable")]
822    procedures: Vec<ProcedureFn>,
823    /// The client specific views of the module.
824    views: Vec<ViewFn>,
825    /// The anonymous views of the module.
826    views_anon: Vec<AnonymousFn>,
827}
828
829// Not actually a mutex; because WASM is single-threaded this basically just turns into a refcell.
830static DESCRIBERS: Mutex<Vec<Box<dyn DescriberFn>>> = Mutex::new(Vec::new());
831
832/// A reducer function takes in `(ReducerContext, Args)`
833/// and returns a result with a possible error message.
834pub type ReducerFn = fn(ReducerContext, &[u8]) -> ReducerResult;
835static REDUCERS: OnceLock<Vec<ReducerFn>> = OnceLock::new();
836
837#[cfg(feature = "unstable")]
838pub type ProcedureFn = fn(ProcedureContext, &[u8]) -> ProcedureResult;
839#[cfg(feature = "unstable")]
840static PROCEDURES: OnceLock<Vec<ProcedureFn>> = OnceLock::new();
841
842/// A view function takes in `(ViewContext, Args)` and returns a Vec of bytes.
843pub type ViewFn = fn(ViewContext, &[u8]) -> Vec<u8>;
844static VIEWS: OnceLock<Vec<ViewFn>> = OnceLock::new();
845
846/// An anonymous view function takes in `(AnonymousViewContext, Args)` and returns a Vec of bytes.
847pub type AnonymousFn = fn(AnonymousViewContext, &[u8]) -> Vec<u8>;
848static ANONYMOUS_VIEWS: OnceLock<Vec<AnonymousFn>> = OnceLock::new();
849
850/// Called by the host when the module is initialized
851/// to describe the module into a serialized form that is returned.
852///
853/// This is also the module's opportunity to ready `__call_reducer__`
854/// (by writing the set of `REDUCERS`).
855///
856/// To `description`, a BSATN-encoded ModuleDef` should be written,.
857/// For the time being, the definition of `ModuleDef` is not stabilized,
858/// as it is being changed by the schema proposal.
859///
860/// The `ModuleDef` is used to define tables, constraints, indexes, reducers, etc.
861/// This affords the module the opportunity
862/// to define and, to a limited extent, alter the schema at initialization time,
863/// including when modules are updated (re-publishing).
864/// After initialization, the module cannot alter the schema.
865#[no_mangle]
866extern "C" fn __describe_module__(description: BytesSink) {
867    // Collect the `module`.
868    let mut module = ModuleBuilder::default();
869    for describer in &mut *DESCRIBERS.lock().unwrap() {
870        describer(&mut module)
871    }
872
873    // Serialize the module to bsatn.
874    let module_def = module.inner.finish();
875    let module_def = RawModuleDef::V9(module_def);
876    let bytes = bsatn::to_vec(&module_def).expect("unable to serialize typespace");
877
878    // Write the sets of reducers, procedures and views.
879    REDUCERS.set(module.reducers).ok().unwrap();
880    #[cfg(feature = "unstable")]
881    PROCEDURES.set(module.procedures).ok().unwrap();
882    VIEWS.set(module.views).ok().unwrap();
883    ANONYMOUS_VIEWS.set(module.views_anon).ok().unwrap();
884
885    // Write the bsatn data into the sink.
886    write_to_sink(description, &bytes);
887}
888
889// TODO(1.0): update `__call_reducer__` docs + for `BytesSink`.
890
891/// Called by the host to execute a reducer
892/// when the `sender` calls the reducer identified by `id` at `timestamp` with `args`.
893///
894/// The `sender_{0-3}` are the pieces of a `[u8; 32]` (`u256`) representing the sender's `Identity`.
895/// They are encoded as follows (assuming `identity.to_byte_array(): [u8; 32]`):
896/// - `sender_0` contains bytes `[0 ..8 ]`.
897/// - `sender_1` contains bytes `[8 ..16]`.
898/// - `sender_2` contains bytes `[16..24]`.
899/// - `sender_3` contains bytes `[24..32]`.
900///
901/// Note that `to_byte_array` uses LITTLE-ENDIAN order! This matches most host systems.
902///
903/// The `conn_id_{0-1}` are the pieces of a `[u8; 16]` (`u128`) representing the callers's [`ConnectionId`].
904/// They are encoded as follows (assuming `conn_id.as_le_byte_array(): [u8; 16]`):
905/// - `conn_id_0` contains bytes `[0 ..8 ]`.
906/// - `conn_id_1` contains bytes `[8 ..16]`.
907///
908/// Again, note that `to_byte_array` uses LITTLE-ENDIAN order! This matches most host systems.
909///
910/// The `args` is a `BytesSource`, registered on the host side,
911/// which can be read with `bytes_source_read`.
912/// The contents of the buffer are the BSATN-encoding of the arguments to the reducer.
913/// In the case of empty arguments, `args` will be 0, that is, invalid.
914///
915/// The `error` is a `BytesSink`, registered on the host side,
916/// which can be written to with `bytes_sink_write`.
917/// When `error` is written to,
918/// it is expected that `HOST_CALL_FAILURE` is returned.
919/// Otherwise, `0` should be returned, i.e., the reducer completed successfully.
920/// Note that in the future, more failure codes could be supported.
921#[no_mangle]
922extern "C" fn __call_reducer__(
923    id: usize,
924    sender_0: u64,
925    sender_1: u64,
926    sender_2: u64,
927    sender_3: u64,
928    conn_id_0: u64,
929    conn_id_1: u64,
930    timestamp: u64,
931    args: BytesSource,
932    error: BytesSink,
933) -> i16 {
934    // Piece together `sender_i` into an `Identity`.
935    let sender = reconstruct_sender_identity(sender_0, sender_1, sender_2, sender_3);
936
937    // Piece together `conn_id_i` into a `ConnectionId`.
938    // The all-zeros `ConnectionId` (`ConnectionId::ZERO`) is interpreted as `None`.
939    let conn_id = reconstruct_connection_id(conn_id_0, conn_id_1);
940
941    // Assemble the `ReducerContext`.
942    let timestamp = Timestamp::from_micros_since_unix_epoch(timestamp as i64);
943    let ctx = ReducerContext::new(crate::Local {}, sender, conn_id, timestamp);
944
945    // Fetch reducer function.
946    let reducers = REDUCERS.get().unwrap();
947    // Dispatch to it with the arguments read.
948    let res = with_read_args(args, |args| reducers[id](ctx, args));
949    // Convert any error message to an error code and writes to the `error` sink.
950    convert_err_to_errno(res, error)
951}
952
953/// Reconstruct the `sender_i` args to [`__call_reducer__`] and [`__call_procedure__`] into an [`Identity`].
954fn reconstruct_sender_identity(sender_0: u64, sender_1: u64, sender_2: u64, sender_3: u64) -> Identity {
955    let sender = [sender_0, sender_1, sender_2, sender_3];
956    let sender: [u8; 32] = bytemuck::must_cast(sender);
957    Identity::from_byte_array(sender) // The LITTLE-ENDIAN constructor.
958}
959
960/// Reconstruct the `conn_id_i` args to [`__call_reducer__`] and [`__call_procedure__`] into a [`ConnectionId`].
961///
962/// The all-zeros `ConnectionId` (`ConnectionId::ZERO`) is interpreted as `None`.
963fn reconstruct_connection_id(conn_id_0: u64, conn_id_1: u64) -> Option<ConnectionId> {
964    // Piece together `conn_id_i` into a `ConnectionId`.
965    // The all-zeros `ConnectionId` (`ConnectionId::ZERO`) is interpreted as `None`.
966    let conn_id = [conn_id_0, conn_id_1];
967    let conn_id: [u8; 16] = bytemuck::must_cast(conn_id);
968    let conn_id = ConnectionId::from_le_byte_array(conn_id); // The LITTLE-ENDIAN constructor.
969    (conn_id != ConnectionId::ZERO).then_some(conn_id)
970}
971
972/// If `res` is `Err`, write the message to `out` and return non-zero.
973/// If `res` is `Ok`, return zero.
974///
975/// Called by [`__call_reducer__`] and [`__call_procedure__`]
976/// to convert the user-returned `Result` into a low-level errno return.
977fn convert_err_to_errno(res: Result<(), Box<str>>, out: BytesSink) -> i16 {
978    match res {
979        Ok(()) => 0,
980        Err(msg) => {
981            write_to_sink(out, msg.as_bytes());
982            errno::HOST_CALL_FAILURE.get() as i16
983        }
984    }
985}
986
987/// Called by the host to execute a procedure
988/// when the `sender` calls the procedure identified by `id` at `timestamp` with `args`.
989///
990/// The `sender_{0-3}` are the pieces of a `[u8; 32]` (`u256`) representing the sender's `Identity`.
991/// They are encoded as follows (assuming `identity.to_byte_array(): [u8; 32]`):
992/// - `sender_0` contains bytes `[0 ..8 ]`.
993/// - `sender_1` contains bytes `[8 ..16]`.
994/// - `sender_2` contains bytes `[16..24]`.
995/// - `sender_3` contains bytes `[24..32]`.
996///
997/// Note that `to_byte_array` uses LITTLE-ENDIAN order! This matches most host systems.
998///
999/// The `conn_id_{0-1}` are the pieces of a `[u8; 16]` (`u128`) representing the callers's [`ConnectionId`].
1000/// They are encoded as follows (assuming `conn_id.as_le_byte_array(): [u8; 16]`):
1001/// - `conn_id_0` contains bytes `[0 ..8 ]`.
1002/// - `conn_id_1` contains bytes `[8 ..16]`.
1003///
1004/// Again, note that `to_byte_array` uses LITTLE-ENDIAN order! This matches most host systems.
1005///
1006/// The `args` is a `BytesSource`, registered on the host side,
1007/// which can be read with `bytes_source_read`.
1008/// The contents of the buffer are the BSATN-encoding of the arguments to the reducer.
1009/// In the case of empty arguments, `args` will be 0, that is, invalid.
1010///
1011/// The `result_sink` is a `BytesSink`, registered on the host side,
1012/// which can be written to with `bytes_sink_write`.
1013/// Procedures are expected to always write to this sink
1014/// the BSATN-serialized bytes of a value of the procedure's return type.
1015///
1016/// Procedures always return the error 0. All other return values are reserved.
1017#[cfg(feature = "unstable")]
1018#[no_mangle]
1019extern "C" fn __call_procedure__(
1020    id: usize,
1021    sender_0: u64,
1022    sender_1: u64,
1023    sender_2: u64,
1024    sender_3: u64,
1025    conn_id_0: u64,
1026    conn_id_1: u64,
1027    timestamp: u64,
1028    args: BytesSource,
1029    result_sink: BytesSink,
1030) -> i16 {
1031    // Piece together `sender_i` into an `Identity`.
1032    let sender = reconstruct_sender_identity(sender_0, sender_1, sender_2, sender_3);
1033
1034    // Piece together `conn_id_i` into a `ConnectionId`.
1035    let conn_id = reconstruct_connection_id(conn_id_0, conn_id_1);
1036
1037    let timestamp = Timestamp::from_micros_since_unix_epoch(timestamp as i64);
1038
1039    // Assemble the `ProcedureContext`.
1040    let ctx = ProcedureContext::new(sender, conn_id, timestamp);
1041
1042    // Grab the list of procedures, which is populated by the preinit functions.
1043    let procedures = PROCEDURES.get().unwrap();
1044
1045    // Deserialize the args and pass them to the actual procedure.
1046    let res = with_read_args(args, |args| procedures[id](ctx, args));
1047
1048    // Write the result bytes to the `result_sink`.
1049    write_to_sink(result_sink, &res);
1050
1051    // Return 0 for no error. Procedures always either trap or return 0.
1052    0
1053}
1054
1055/// Called by the host to execute an anonymous view.
1056///
1057/// The `args` is a `BytesSource`, registered on the host side,
1058/// which can be read with `bytes_source_read`.
1059/// The contents of the buffer are the BSATN-encoding of the arguments to the view.
1060/// In the case of empty arguments, `args` will be 0, that is, invalid.
1061///
1062/// The output of the view is written to a `BytesSink`,
1063/// registered on the host side, with `bytes_sink_write`.
1064///
1065/// Note, a previous version of the abi used a different return format for views.
1066/// We used to write the return rows of the view directly to the sink.
1067/// However the current version first writes a [`ViewResultHeader`].
1068/// This is to distinguish between views that return rows vs ones that return queries.
1069///
1070/// The current abi is identified by a return code of 2.
1071/// The previous abi, which we still support, is identified by a return code of 0.
1072#[no_mangle]
1073extern "C" fn __call_view_anon__(id: usize, args: BytesSource, sink: BytesSink) -> i16 {
1074    let views = ANONYMOUS_VIEWS.get().unwrap();
1075    write_to_sink(
1076        sink,
1077        &with_read_args(args, |args| views[id](AnonymousViewContext::default(), args)),
1078    );
1079    2
1080}
1081
1082/// Called by the host to execute a view when the `sender` calls the view identified by `id` with `args`.
1083/// See [`__call_reducer__`] for more commentary on the arguments.
1084///
1085/// The `args` is a `BytesSource`, registered on the host side,
1086/// which can be read with `bytes_source_read`.
1087/// The contents of the buffer are the BSATN-encoding of the arguments to the view.
1088/// In the case of empty arguments, `args` will be 0, that is, invalid.
1089///
1090/// The output of the view is written to a `BytesSink`,
1091/// registered on the host side, with `bytes_sink_write`.
1092///
1093/// Note, a previous version of the abi used a different return format for views.
1094/// We used to write the return rows of the view directly to the sink.
1095/// However the current version first writes a [`ViewResultHeader`].
1096/// This is to distinguish between views that return rows vs ones that return queries.
1097///
1098/// The current abi is identified by a return code of 2.
1099/// The previous abi, which we still support, is identified by a return code of 0.
1100#[no_mangle]
1101extern "C" fn __call_view__(
1102    id: usize,
1103    sender_0: u64,
1104    sender_1: u64,
1105    sender_2: u64,
1106    sender_3: u64,
1107    args: BytesSource,
1108    sink: BytesSink,
1109) -> i16 {
1110    // Piece together `sender_i` into an `Identity`.
1111    let sender = [sender_0, sender_1, sender_2, sender_3];
1112    let sender: [u8; 32] = bytemuck::must_cast(sender);
1113    let sender = Identity::from_byte_array(sender); // The LITTLE-ENDIAN constructor.
1114
1115    let views = VIEWS.get().unwrap();
1116
1117    write_to_sink(
1118        sink,
1119        &with_read_args(args, |args| views[id](ViewContext::new(sender), args)),
1120    );
1121    2
1122}
1123
1124/// Run `logic` with `args` read from the host into a `&[u8]`.
1125fn with_read_args<R>(args: BytesSource, logic: impl FnOnce(&[u8]) -> R) -> R {
1126    if args == BytesSource::INVALID {
1127        return logic(&[]);
1128    }
1129
1130    // Steal an iteration row buffer.
1131    // These were not meant for this purpose,
1132    // but it's likely we have one sitting around being unused at this point,
1133    // so use it to avoid allocating a temporary buffer if possible.
1134    // And if we do allocate a temporary buffer now, it will likely be reused later.
1135    let mut buf = IterBuf::take();
1136
1137    // Read `args` and run `logic`.
1138    read_bytes_source_into(args, &mut buf);
1139    logic(&buf)
1140}
1141
1142const NO_SPACE: u16 = errno::NO_SPACE.get();
1143const NO_SUCH_BYTES: u16 = errno::NO_SUCH_BYTES.get();
1144
1145/// Look up the jwt associated with `connection_id`.
1146pub fn get_jwt(connection_id: ConnectionId) -> Option<String> {
1147    let mut buf = IterBuf::take();
1148    let source = sys::get_jwt(connection_id.as_le_byte_array())?;
1149    if source == BytesSource::INVALID {
1150        return None;
1151    }
1152    read_bytes_source_into(source, &mut buf);
1153    Some(std::str::from_utf8(&buf).unwrap().to_string())
1154}
1155
1156/// Read `source` from the host fully into `buf`.
1157pub(crate) fn read_bytes_source_into(source: BytesSource, buf: &mut Vec<u8>) {
1158    const INVALID: i16 = NO_SUCH_BYTES as i16;
1159
1160    // For reducer arguments, the `buf` will almost certainly already be large enough,
1161    // as it comes from `IterBuf`, which start at 64KiB.
1162    // But reading the remaining length and calling `buf.reserve` is a negligible cost,
1163    // and in the future we may want to use this method to read other `BytesSource`s into other buffers.
1164    // I (pgoldman 2025-09-26) also value having it as an example of correct usage of `bytes_source_remaining_length`.
1165    let len = {
1166        let mut len = 0;
1167        let ret = unsafe { sys::raw::bytes_source_remaining_length(source, &raw mut len) };
1168        match ret {
1169            0 => len,
1170            INVALID => panic!("invalid source passed"),
1171            _ => unreachable!(),
1172        }
1173    };
1174    buf.reserve(buf.len().saturating_sub(len as usize));
1175
1176    // Because we've reserved space in our buffer already, this loop should be unnecessary.
1177    // We expect the first call to `bytes_source_read` to always return `-1`.
1178    // I (pgoldman 2025-09-26) am leaving the loop here because there's no downside to it,
1179    // and in the future we may want to support `BytesSource`s which don't have a known length ahead of time
1180    // (i.e. put arbitrary streams in `BytesSource` on the host side rather than just `Bytes` buffers),
1181    // at which point the loop will become useful again.
1182    loop {
1183        // Write into the spare capacity of the buffer.
1184        let buf_ptr = buf.spare_capacity_mut();
1185        let spare_len = buf_ptr.len();
1186        let mut buf_len = buf_ptr.len();
1187        let buf_ptr = buf_ptr.as_mut_ptr().cast();
1188        let ret = unsafe { sys::raw::bytes_source_read(source, buf_ptr, &mut buf_len) };
1189        if ret <= 0 {
1190            // SAFETY: `bytes_source_read` just appended `buf_len` bytes to `buf`.
1191            unsafe { buf.set_len(buf.len() + buf_len) };
1192        }
1193        match ret {
1194            // Host side source exhausted, we're done.
1195            -1 => break,
1196            // Wrote the entire spare capacity.
1197            // Need to reserve more space in the buffer.
1198            0 if spare_len == buf_len => buf.reserve(1024),
1199            // Host didn't write as much as possible.
1200            // Try to read some more.
1201            // The host will likely not trigger this branch (current host doesn't),
1202            // but a module should be prepared for it.
1203            0 => {}
1204            INVALID => panic!("invalid source passed"),
1205            _ => unreachable!(),
1206        }
1207    }
1208}
1209
1210/// Write `buf` to `sink`.
1211fn write_to_sink(sink: BytesSink, mut buf: &[u8]) {
1212    loop {
1213        let len = &mut buf.len();
1214        match unsafe { sys::raw::bytes_sink_write(sink, buf.as_ptr(), len) } {
1215            0 => {
1216                // Set `buf` to remainder and bail if it's empty.
1217                (_, buf) = buf.split_at(*len);
1218                if buf.is_empty() {
1219                    break;
1220                }
1221            }
1222            NO_SUCH_BYTES => panic!("invalid sink passed"),
1223            NO_SPACE => panic!("no space left at sink"),
1224            _ => unreachable!(),
1225        }
1226    }
1227}
1228
1229#[macro_export]
1230#[doc(hidden)]
1231macro_rules! __make_register_reftype {
1232    ($ty:ty, $name:literal) => {
1233        const _: () = {
1234            #[export_name = concat!("__preinit__20_register_describer_", $name)]
1235            extern "C" fn __register_describer() {
1236                $crate::rt::register_reftype::<$ty>()
1237            }
1238        };
1239    };
1240}
1241
1242#[cfg(feature = "unstable")]
1243#[doc(hidden)]
1244pub fn volatile_nonatomic_schedule_immediate<'de, A: Args<'de>, R: Reducer<'de, A>, R2: FnInfo<Invoke = ReducerFn>>(
1245    _reducer: R,
1246    args: A,
1247) {
1248    let arg_bytes = bsatn::to_vec(&SerDeArgs(args)).unwrap();
1249
1250    // Schedule the reducer.
1251    sys::volatile_nonatomic_schedule_immediate(R2::NAME, &arg_bytes)
1252}
1253
1254/// Read `source` completely into a temporary buffer, then BSATN-deserialize it as a `T`.
1255///
1256/// Panics if the bytes from `source` fail to deserialize as `T`.
1257/// The type name of `T` will be included in the panic message.
1258#[cfg_attr(not(feature = "unstable"), allow(unused))]
1259pub(crate) fn read_bytes_source_as<T: DeserializeOwned + 'static>(source: BytesSource) -> T {
1260    let mut buf = IterBuf::take();
1261    read_bytes_source_into(source, &mut buf);
1262    bsatn::from_slice::<T>(&buf)
1263        .unwrap_or_else(|err| panic!("Failed to BSATN-deserialize `{}`: {err:#?}", std::any::type_name::<T>()))
1264}