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