1use anyhow::Context;
2use bytes::Bytes;
3use bytestring::ByteString;
4use derive_more::Display;
5use enum_map::Enum;
6use once_cell::sync::OnceCell;
7use spacetimedb_lib::bsatn;
8use spacetimedb_lib::de::serde::SeedWrapper;
9use spacetimedb_lib::de::DeserializeSeed;
10use spacetimedb_lib::ProductValue;
11use spacetimedb_schema::def::deserialize::ReducerArgsDeserializeSeed;
12
13mod disk_storage;
14mod host_controller;
15#[allow(clippy::too_many_arguments)]
16pub mod module_host;
17pub mod scheduler;
18pub mod wasmtime;
19pub mod instance_env;
21mod wasm_common;
22
23pub use disk_storage::DiskStorage;
24pub use host_controller::{
25 extract_schema, DurabilityProvider, ExternalDurability, ExternalStorage, HostController, ProgramStorage,
26 ReducerCallResult, ReducerOutcome, StartSnapshotWatcher,
27};
28pub use module_host::{ModuleHost, NoSuchModule, ReducerCallError, UpdateDatabaseResult};
29pub use scheduler::Scheduler;
30
31#[derive(Debug)]
32pub enum ReducerArgs {
33 Json(ByteString),
34 Bsatn(Bytes),
35 Nullary,
36}
37
38impl ReducerArgs {
39 fn into_tuple(self, seed: ReducerArgsDeserializeSeed) -> Result<ArgsTuple, InvalidReducerArguments> {
40 self._into_tuple(seed).map_err(|err| InvalidReducerArguments {
41 err,
42 reducer: (*seed.reducer_def().name).into(),
43 })
44 }
45 fn _into_tuple(self, seed: ReducerArgsDeserializeSeed) -> anyhow::Result<ArgsTuple> {
46 Ok(match self {
47 ReducerArgs::Json(json) => ArgsTuple {
48 tuple: from_json_seed(&json, SeedWrapper(seed))?,
49 bsatn: OnceCell::new(),
50 json: OnceCell::with_value(json),
51 },
52 ReducerArgs::Bsatn(bytes) => ArgsTuple {
53 tuple: seed.deserialize(bsatn::Deserializer::new(&mut &bytes[..]))?,
54 bsatn: OnceCell::with_value(bytes),
55 json: OnceCell::new(),
56 },
57 ReducerArgs::Nullary => {
58 anyhow::ensure!(
59 seed.reducer_def().params.elements.is_empty(),
60 "failed to typecheck args"
61 );
62 ArgsTuple::nullary()
63 }
64 })
65 }
66}
67
68#[derive(Debug, Clone)]
69pub struct ArgsTuple {
70 tuple: ProductValue,
71 bsatn: OnceCell<Bytes>,
72 json: OnceCell<ByteString>,
73}
74
75impl ArgsTuple {
76 pub fn nullary() -> Self {
77 ArgsTuple {
78 tuple: spacetimedb_sats::product![],
79 bsatn: OnceCell::with_value(Bytes::new()),
80 json: OnceCell::with_value(ByteString::from_static("[]")),
81 }
82 }
83
84 pub fn get_bsatn(&self) -> &Bytes {
85 self.bsatn.get_or_init(|| bsatn::to_vec(&self.tuple).unwrap().into())
86 }
87 pub fn get_json(&self) -> &ByteString {
88 use spacetimedb_sats::ser::serde::SerializeWrapper;
89 self.json.get_or_init(|| {
90 serde_json::to_string(SerializeWrapper::from_ref(&self.tuple))
91 .unwrap()
92 .into()
93 })
94 }
95}
96
97impl Default for ArgsTuple {
98 fn default() -> Self {
99 Self::nullary()
100 }
101}
102
103pub use spacetimedb_primitives::ReducerId;
105
106#[derive(thiserror::Error, Debug)]
107#[error("invalid arguments for reducer {reducer}: {err}")]
108pub struct InvalidReducerArguments {
109 #[source]
110 err: anyhow::Error,
111 reducer: Box<str>,
112}
113
114fn from_json_seed<'de, T: serde::de::DeserializeSeed<'de>>(s: &'de str, seed: T) -> anyhow::Result<T::Value> {
115 let mut de = serde_json::Deserializer::from_str(s);
116 let mut track = serde_path_to_error::Track::new();
117 let out = seed
118 .deserialize(serde_path_to_error::Deserializer::new(&mut de, &mut track))
119 .context(track.path())?;
120 de.end()?;
121 Ok(out)
122}
123
124#[derive(Debug, Display, Enum, Clone, Copy, strum::AsRefStr)]
126pub enum AbiCall {
127 TableIdFromName,
128 IndexIdFromName,
129 DatastoreTableRowCount,
130 DatastoreTableScanBsatn,
131 DatastoreIndexScanRangeBsatn,
132 RowIterBsatnAdvance,
133 RowIterBsatnClose,
134 DatastoreInsertBsatn,
135 DatastoreUpdateBsatn,
136 DatastoreDeleteByIndexScanRangeBsatn,
137 DatastoreDeleteAllByEqBsatn,
138 BytesSourceRead,
139 BytesSinkWrite,
140 ConsoleLog,
141 ConsoleTimerStart,
142 ConsoleTimerEnd,
143 Identity,
144
145 VolatileNonatomicScheduleImmediate,
146}