spacetimedb/host/
mod.rs

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;
19// Visible for integration testing.
20pub 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
103// TODO(noa): replace imports from this module with imports straight from primitives.
104pub 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/// Tags for each call that a `WasmInstanceEnv` can make.
125#[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}