spacetimedb_bindings_sys/
lib.rs

1//! Defines sys calls to interact with SpacetimeDB.
2//! This forms an ABI of sorts that modules written in Rust can use.
3
4extern crate alloc;
5
6use core::fmt;
7use core::mem::MaybeUninit;
8use core::num::NonZeroU16;
9use std::ptr;
10
11use spacetimedb_primitives::{errno, errnos, ColId, IndexId, TableId};
12
13/// Provides a raw set of sys calls which abstractions can be built atop of.
14pub mod raw {
15    use spacetimedb_primitives::{ColId, IndexId, TableId};
16
17    // this module identifier determines the abi version that modules built with this crate depend
18    // on. Any non-breaking additions to the abi surface should be put in a new `extern {}` block
19    // with a module identifier with a minor version 1 above the previous highest minor version.
20    // For breaking changes, all functions should be moved into one new `spacetime_X.0` block.
21    #[link(wasm_import_module = "spacetime_10.0")]
22    extern "C" {
23        /// Queries the `table_id` associated with the given (table) `name`
24        /// where `name` is the UTF-8 slice in WASM memory at `name_ptr[..name_len]`.
25        ///
26        /// The table id is written into the `out` pointer.
27        ///
28        /// # Traps
29        ///
30        /// Traps if:
31        /// - `name_ptr` is NULL or `name` is not in bounds of WASM memory.
32        /// - `name` is not valid UTF-8.
33        /// - `out` is NULL or `out[..size_of::<TableId>()]` is not in bounds of WASM memory.
34        ///
35        /// # Errors
36        ///
37        /// Returns an error:
38        ///
39        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
40        /// - `NO_SUCH_TABLE`, when `name` is not the name of a table.
41        pub fn table_id_from_name(name: *const u8, name_len: usize, out: *mut TableId) -> u16;
42
43        /// Queries the `index_id` associated with the given (index) `name`
44        /// where `name` is the UTF-8 slice in WASM memory at `name_ptr[..name_len]`.
45        ///
46        /// If a `name` was provided for the `RawIndexDef` for this index, that is the name of the index.
47        /// If no name was provided, a name was autogenerated like so:
48        /// ```
49        /// let table_name: String = "my_table".into();
50        /// let column_names: Vec<String> = vec!["bananas".into(), "oranges".into()];
51        /// let column_names = column_names.join("_");
52        /// let name = format!("{table_name}_{column_names}_idx_btree");
53        /// ```
54        /// (See the function `spacetimedb_schema::def::validate::v9::generate_index_name` for more
55        /// information.)
56        ///
57        /// The index id is written into the `out` pointer.
58        ///
59        /// # Traps
60        ///
61        /// Traps if:
62        /// - `name_ptr` is NULL or `name` is not in bounds of WASM memory.
63        /// - `name` is not valid UTF-8.
64        /// - `out` is NULL or `out[..size_of::<IndexId>()]` is not in bounds of WASM memory.
65        ///
66        /// # Errors
67        ///
68        /// Returns an error:
69        ///
70        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
71        /// - `NO_SUCH_INDEX`, when `name` is not the name of an index.
72        pub fn index_id_from_name(name_ptr: *const u8, name_len: usize, out: *mut IndexId) -> u16;
73
74        /// Writes the number of rows currently in table identified by `table_id` to `out`.
75        ///
76        /// # Traps
77        ///
78        /// Traps if:
79        /// - `out` is NULL or `out[..size_of::<u64>()]` is not in bounds of WASM memory.
80        ///
81        /// # Errors
82        ///
83        /// Returns an error:
84        ///
85        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
86        /// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.
87        pub fn datastore_table_row_count(table_id: TableId, out: *mut u64) -> u16;
88
89        /// Starts iteration on each row, as BSATN-encoded, of a table identified by `table_id`.
90        ///
91        /// On success, the iterator handle is written to the `out` pointer.
92        /// This handle can be advanced by [`row_iter_bsatn_advance`].
93        ///
94        /// # Traps
95        ///
96        /// This function does not trap.
97        ///
98        /// # Errors
99        ///
100        /// Returns an error:
101        ///
102        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
103        /// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.
104        pub fn datastore_table_scan_bsatn(table_id: TableId, out: *mut RowIter) -> u16;
105
106        /// Finds all rows in the index identified by `index_id`,
107        /// according to the:
108        /// - `prefix = prefix_ptr[..prefix_len]`,
109        /// - `rstart = rstart_ptr[..rstart_len]`,
110        /// - `rend = rend_ptr[..rend_len]`,
111        ///
112        /// in WASM memory.
113        ///
114        /// The index itself has a schema/type.
115        /// The `prefix` is decoded to the initial `prefix_elems` `AlgebraicType`s
116        /// whereas `rstart` and `rend` are decoded to the `prefix_elems + 1` `AlgebraicType`
117        /// where the `AlgebraicValue`s are wrapped in `Bound`.
118        /// That is, `rstart, rend` are BSATN-encoded `Bound<AlgebraicValue>`s.
119        ///
120        /// Matching is then defined by equating `prefix`
121        /// to the initial `prefix_elems` columns of the index
122        /// and then imposing `rstart` as the starting bound
123        /// and `rend` as the ending bound on the `prefix_elems + 1` column of the index.
124        /// Remaining columns of the index are then unbounded.
125        /// Note that the `prefix` in this case can be empty (`prefix_elems = 0`),
126        /// in which case this becomes a ranged index scan on a single-col index
127        /// or even a full table scan if `rstart` and `rend` are both unbounded.
128        ///
129        /// The relevant table for the index is found implicitly via the `index_id`,
130        /// which is unique for the module.
131        ///
132        /// On success, the iterator handle is written to the `out` pointer.
133        /// This handle can be advanced by [`row_iter_bsatn_advance`].
134        ///
135        /// # Non-obvious queries
136        ///
137        /// For an index on columns `[a, b, c]`:
138        ///
139        /// - `a = x, b = y` is encoded as a prefix `[x, y]`
140        ///   and a range `Range::Unbounded`,
141        ///   or as a  prefix `[x]` and a range `rstart = rend = Range::Inclusive(y)`.
142        /// - `a = x, b = y, c = z` is encoded as a prefix `[x, y]`
143        ///   and a  range `rstart = rend = Range::Inclusive(z)`.
144        /// - A sorted full scan is encoded as an empty prefix
145        ///   and a range `Range::Unbounded`.
146        ///
147        /// # Traps
148        ///
149        /// Traps if:
150        /// - `prefix_elems > 0`
151        ///   and (`prefix_ptr` is NULL or `prefix` is not in bounds of WASM memory).
152        /// - `rstart` is NULL or `rstart` is not in bounds of WASM memory.
153        /// - `rend` is NULL or `rend` is not in bounds of WASM memory.
154        /// - `out` is NULL or `out[..size_of::<RowIter>()]` is not in bounds of WASM memory.
155        ///
156        /// # Errors
157        ///
158        /// Returns an error:
159        ///
160        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
161        /// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
162        /// - `WRONG_INDEX_ALGO` if the index is not a range-compatible index.
163        /// - `BSATN_DECODE_ERROR`, when `prefix` cannot be decoded to
164        ///   a `prefix_elems` number of `AlgebraicValue`
165        ///   typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.
166        ///   Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`
167        ///   where the inner `AlgebraicValue`s are
168        ///   typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.
169        pub fn datastore_index_scan_range_bsatn(
170            index_id: IndexId,
171            prefix_ptr: *const u8,
172            prefix_len: usize,
173            prefix_elems: ColId,
174            rstart_ptr: *const u8, // Bound<AlgebraicValue>
175            rstart_len: usize,
176            rend_ptr: *const u8, // Bound<AlgebraicValue>
177            rend_len: usize,
178            out: *mut RowIter,
179        ) -> u16;
180
181        /// This is the same as [`datastore_index_scan_range_bsatn`].
182        #[deprecated = "use `datastore_index_scan_range_bsatn` instead"]
183        #[doc(alias = "datastore_index_scan_range_bsatn")]
184        pub fn datastore_btree_scan_bsatn(
185            index_id: IndexId,
186            prefix_ptr: *const u8,
187            prefix_len: usize,
188            prefix_elems: ColId,
189            rstart_ptr: *const u8, // Bound<AlgebraicValue>
190            rstart_len: usize,
191            rend_ptr: *const u8, // Bound<AlgebraicValue>
192            rend_len: usize,
193            out: *mut RowIter,
194        ) -> u16;
195
196        /// Deletes all rows found in the index identified by `index_id`,
197        /// according to the:
198        /// - `prefix = prefix_ptr[..prefix_len]`,
199        /// - `rstart = rstart_ptr[..rstart_len]`,
200        /// - `rend = rend_ptr[..rend_len]`,
201        ///
202        /// in WASM memory.
203        ///
204        /// This syscall will delete all the rows found by
205        /// [`datastore_index_scan_range_bsatn`] with the same arguments passed,
206        /// including `prefix_elems`.
207        /// See `datastore_index_scan_range_bsatn` for details.
208        ///
209        /// The number of rows deleted is written to the WASM pointer `out`.
210        ///
211        /// # Traps
212        ///
213        /// Traps if:
214        /// - `prefix_elems > 0`
215        ///   and (`prefix_ptr` is NULL or `prefix` is not in bounds of WASM memory).
216        /// - `rstart` is NULL or `rstart` is not in bounds of WASM memory.
217        /// - `rend` is NULL or `rend` is not in bounds of WASM memory.
218        /// - `out` is NULL or `out[..size_of::<u32>()]` is not in bounds of WASM memory.
219        ///
220        /// # Errors
221        ///
222        /// Returns an error:
223        ///
224        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
225        /// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
226        /// - `WRONG_INDEX_ALGO` if the index is not a range-compatible index.
227        /// - `BSATN_DECODE_ERROR`, when `prefix` cannot be decoded to
228        ///   a `prefix_elems` number of `AlgebraicValue`
229        ///   typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.
230        ///   Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`
231        ///   where the inner `AlgebraicValue`s are
232        ///   typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.
233        pub fn datastore_delete_by_index_scan_range_bsatn(
234            index_id: IndexId,
235            prefix_ptr: *const u8,
236            prefix_len: usize,
237            prefix_elems: ColId,
238            rstart_ptr: *const u8, // Bound<AlgebraicValue>
239            rstart_len: usize,
240            rend_ptr: *const u8, // Bound<AlgebraicValue>
241            rend_len: usize,
242            out: *mut u32,
243        ) -> u16;
244
245        /// This is the same as [`datastore_delete_by_index_scan_range_bsatn`].
246        #[deprecated = "use `datastore_delete_by_index_scan_range_bsatn` instead"]
247        #[doc(alias = "datastore_delete_by_index_scan_range_bsatn")]
248        pub fn datastore_delete_by_btree_scan_bsatn(
249            index_id: IndexId,
250            prefix_ptr: *const u8,
251            prefix_len: usize,
252            prefix_elems: ColId,
253            rstart_ptr: *const u8, // Bound<AlgebraicValue>
254            rstart_len: usize,
255            rend_ptr: *const u8, // Bound<AlgebraicValue>
256            rend_len: usize,
257            out: *mut u32,
258        ) -> u16;
259
260        /// Deletes those rows, in the table identified by `table_id`,
261        /// that match any row in the byte string `rel = rel_ptr[..rel_len]` in WASM memory.
262        ///
263        /// Matching is defined by first BSATN-decoding
264        /// the byte string pointed to at by `relation` to a `Vec<ProductValue>`
265        /// according to the row schema of the table
266        /// and then using `Ord for AlgebraicValue`.
267        /// A match happens when `Ordering::Equal` is returned from `fn cmp`.
268        /// This occurs exactly when the row's BSATN-encoding is equal to the encoding of the `ProductValue`.
269        ///
270        /// The number of rows deleted is written to the WASM pointer `out`.
271        ///
272        /// # Traps
273        ///
274        /// Traps if:
275        /// - `rel_ptr` is NULL or `rel` is not in bounds of WASM memory.
276        /// - `out` is NULL or `out[..size_of::<u32>()]` is not in bounds of WASM memory.
277        ///
278        /// # Errors
279        ///
280        /// Returns an error:
281        ///
282        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
283        /// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.
284        /// - `BSATN_DECODE_ERROR`, when `rel` cannot be decoded to `Vec<ProductValue>`
285        ///   where each `ProductValue` is typed at the `ProductType` the table's schema specifies.
286        pub fn datastore_delete_all_by_eq_bsatn(
287            table_id: TableId,
288            rel_ptr: *const u8,
289            rel_len: usize,
290            out: *mut u32,
291        ) -> u16;
292
293        /// Reads rows from the given iterator registered under `iter`.
294        ///
295        /// Takes rows from the iterator
296        /// and stores them in the memory pointed to by `buffer = buffer_ptr[..buffer_len]`,
297        /// encoded in BSATN format.
298        ///
299        /// The `buffer_len = buffer_len_ptr[..size_of::<usize>()]` stores the capacity of `buffer`.
300        /// On success (`0` or `-1` is returned),
301        /// `buffer_len` is set to the combined length of the encoded rows.
302        /// When `-1` is returned, the iterator has been exhausted
303        /// and there are no more rows to read,
304        /// leading to the iterator being immediately destroyed.
305        /// Note that the host is free to reuse allocations in a pool,
306        /// destroying the handle logically does not entail that memory is necessarily reclaimed.
307        ///
308        /// # Traps
309        ///
310        /// Traps if:
311        ///
312        /// - `buffer_len_ptr` is NULL or `buffer_len` is not in bounds of WASM memory.
313        /// - `buffer_ptr` is NULL or `buffer` is not in bounds of WASM memory.
314        ///
315        /// # Errors
316        ///
317        /// Returns an error:
318        ///
319        /// - `NO_SUCH_ITER`, when `iter` is not a valid iterator.
320        /// - `BUFFER_TOO_SMALL`, when there are rows left but they cannot fit in `buffer`.
321        ///   When this occurs, `buffer_len` is set to the size of the next item in the iterator.
322        ///   To make progress, the caller should reallocate the buffer to at least that size and try again.
323        pub fn row_iter_bsatn_advance(iter: RowIter, buffer_ptr: *mut u8, buffer_len_ptr: *mut usize) -> i16;
324
325        /// Destroys the iterator registered under `iter`.
326        ///
327        /// Once `row_iter_bsatn_close` is called on `iter`, the `iter` is invalid.
328        /// That is, `row_iter_bsatn_close(iter)` the second time will yield `NO_SUCH_ITER`.
329        ///
330        /// # Errors
331        ///
332        /// Returns an error:
333        ///
334        /// - `NO_SUCH_ITER`, when `iter` is not a valid iterator.
335        pub fn row_iter_bsatn_close(iter: RowIter) -> u16;
336
337        /// Inserts a row into the table identified by `table_id`,
338        /// where the row is read from the byte string `row = row_ptr[..row_len]` in WASM memory
339        /// where `row_len = row_len_ptr[..size_of::<usize>()]` stores the capacity of `row`.
340        ///
341        /// The byte string `row` must be a BSATN-encoded `ProductValue`
342        /// typed at the table's `ProductType` row-schema.
343        ///
344        /// To handle auto-incrementing columns,
345        /// when the call is successful,
346        /// the `row` is written back to with the generated sequence values.
347        /// These values are written as a BSATN-encoded `pv: ProductValue`.
348        /// Each `v: AlgebraicValue` in `pv` is typed at the sequence's column type.
349        /// The `v`s in `pv` are ordered by the order of the columns, in the schema of the table.
350        /// When the table has no sequences,
351        /// this implies that the `pv`, and thus `row`, will be empty.
352        /// The `row_len` is set to the length of `bsatn(pv)`.
353        ///
354        /// # Traps
355        ///
356        /// Traps if:
357        /// - `row_len_ptr` is NULL or `row_len` is not in bounds of WASM memory.
358        /// - `row_ptr` is NULL or `row` is not in bounds of WASM memory.
359        ///
360        /// # Errors
361        ///
362        /// Returns an error:
363        ///
364        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
365        /// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.
366        /// - `BSATN_DECODE_ERROR`, when `row` cannot be decoded to a `ProductValue`.
367        ///   typed at the `ProductType` the table's schema specifies.
368        /// - `UNIQUE_ALREADY_EXISTS`, when inserting `row` would violate a unique constraint.
369        /// - `SCHEDULE_AT_DELAY_TOO_LONG`, when the delay specified in the row was too long.
370        pub fn datastore_insert_bsatn(table_id: TableId, row_ptr: *mut u8, row_len_ptr: *mut usize) -> u16;
371
372        /// Updates a row in the table identified by `table_id` to `row`
373        /// where the row is read from the byte string `row = row_ptr[..row_len]` in WASM memory
374        /// where `row_len = row_len_ptr[..size_of::<usize>()]` stores the capacity of `row`.
375        ///
376        /// The byte string `row` must be a BSATN-encoded `ProductValue`
377        /// typed at the table's `ProductType` row-schema.
378        ///
379        /// The row to update is found by projecting `row`
380        /// to the type of the *unique* index identified by `index_id`.
381        /// If no row is found, the error `NO_SUCH_ROW` is returned.
382        ///
383        /// To handle auto-incrementing columns,
384        /// when the call is successful,
385        /// the `row` is written back to with the generated sequence values.
386        /// These values are written as a BSATN-encoded `pv: ProductValue`.
387        /// Each `v: AlgebraicValue` in `pv` is typed at the sequence's column type.
388        /// The `v`s in `pv` are ordered by the order of the columns, in the schema of the table.
389        /// When the table has no sequences,
390        /// this implies that the `pv`, and thus `row`, will be empty.
391        /// The `row_len` is set to the length of `bsatn(pv)`.
392        ///
393        /// # Traps
394        ///
395        /// Traps if:
396        /// - `row_len_ptr` is NULL or `row_len` is not in bounds of WASM memory.
397        /// - `row_ptr` is NULL or `row` is not in bounds of WASM memory.
398        ///
399        /// # Errors
400        ///
401        /// Returns an error:
402        ///
403        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
404        /// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.
405        /// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
406        /// - `INDEX_NOT_UNIQUE`, when the index was not unique.
407        /// - `NO_SUCH_ROW`, when the row was not found in the unique index.
408        /// - `BSATN_DECODE_ERROR`, when `row` cannot be decoded to a `ProductValue`
409        ///   typed at the `ProductType` the table's schema specifies
410        ///   or when it cannot be projected to the index identified by `index_id`.
411        /// - `UNIQUE_ALREADY_EXISTS`, when inserting `row` would violate a unique constraint.
412        /// - `SCHEDULE_AT_DELAY_TOO_LONG`, when the delay specified in the row was too long.
413        pub fn datastore_update_bsatn(
414            table_id: TableId,
415            index_id: IndexId,
416            row_ptr: *mut u8,
417            row_len_ptr: *mut usize,
418        ) -> u16;
419
420        /// Schedules a reducer to be called asynchronously, nonatomically,
421        /// and immediately on a best effort basis.
422        ///
423        /// The reducer is named as the valid UTF-8 slice `(name, name_len)`,
424        /// and is passed the slice `(args, args_len)` as its argument.
425        ///
426        /// Traps if
427        /// - `name` does not point to valid UTF-8
428        /// - `name + name_len` or `args + args_len` overflow a 64-bit integer
429        #[cfg(feature = "unstable")]
430        pub fn volatile_nonatomic_schedule_immediate(
431            name: *const u8,
432            name_len: usize,
433            args: *const u8,
434            args_len: usize,
435        );
436
437        /// Writes up to `buffer_len` bytes from `buffer = buffer_ptr[..buffer_len]`,
438        /// to the `sink`, registered in the host environment.
439        ///
440        /// The `buffer_len = buffer_len_ptr[..size_of::<usize>()]` stores the capacity of `buffer`.
441        /// On success (`0` is returned),
442        /// `buffer_len` is set to the number of bytes written to `sink`.
443        ///
444        /// # Traps
445        ///
446        /// - `buffer_len_ptr` is NULL or `buffer_len` is not in bounds of WASM memory.
447        /// - `buffer_ptr` is NULL or `buffer` is not in bounds of WASM memory.
448        ///
449        /// # Errors
450        ///
451        /// Returns an error:
452        ///
453        /// - `NO_SUCH_BYTES`, when `sink` is not a valid bytes sink.
454        /// - `NO_SPACE`, when there is no room for more bytes in `sink`.
455        pub fn bytes_sink_write(sink: BytesSink, buffer_ptr: *const u8, buffer_len_ptr: *mut usize) -> u16;
456
457        /// Reads bytes from `source`, registered in the host environment,
458        /// and stores them in the memory pointed to by `buffer = buffer_ptr[..buffer_len]`.
459        ///
460        /// The `buffer_len = buffer_len_ptr[..size_of::<usize>()]` stores the capacity of `buffer`.
461        /// On success (`0` or `-1` is returned),
462        /// `buffer_len` is set to the number of bytes written to `buffer`.
463        /// When `-1` is returned, the resource has been exhausted
464        /// and there are no more bytes to read,
465        /// leading to the resource being immediately destroyed.
466        /// Note that the host is free to reuse allocations in a pool,
467        /// destroying the handle logically does not entail that memory is necessarily reclaimed.
468        ///
469        /// # Traps
470        ///
471        /// Traps if:
472        ///
473        /// - `buffer_len_ptr` is NULL or `buffer_len` is not in bounds of WASM memory.
474        /// - `buffer_ptr` is NULL or `buffer` is not in bounds of WASM memory.
475        ///
476        /// # Errors
477        ///
478        /// Returns an error:
479        ///
480        /// - `NO_SUCH_BYTES`, when `source` is not a valid bytes source.
481        ///
482        /// # Example
483        ///
484        /// The typical use case for this ABI is in `__call_reducer__`,
485        /// to read and deserialize the `args`.
486        /// An example definition, dealing with `args` might be:
487        /// ```rust,ignore
488        /// /// #[no_mangle]
489        /// extern "C" fn __call_reducer__(..., args: BytesSource, ...) -> i16 {
490        ///     // ...
491        ///
492        ///     let mut buf = Vec::<u8>::with_capacity(1024);
493        ///     loop {
494        ///         // Write into the spare capacity of the buffer.
495        ///         let buf_ptr = buf.spare_capacity_mut();
496        ///         let spare_len = buf_ptr.len();
497        ///         let mut buf_len = buf_ptr.len();
498        ///         let buf_ptr = buf_ptr.as_mut_ptr().cast();
499        ///         let ret = unsafe { bytes_source_read(args, buf_ptr, &mut buf_len) };
500        ///         // SAFETY: `bytes_source_read` just appended `spare_len` bytes to `buf`.
501        ///         unsafe { buf.set_len(buf.len() + spare_len) };
502        ///         match ret {
503        ///             // Host side source exhausted, we're done.
504        ///             -1 => break,
505        ///             // Wrote the entire spare capacity.
506        ///             // Need to reserve more space in the buffer.
507        ///             0 if spare_len == buf_len => buf.reserve(1024),
508        ///             // Host didn't write as much as possible.
509        ///             // Try to read some more.
510        ///             // The host will likely not trigger this branch,
511        ///             // but a module should be prepared for it.
512        ///             0 => {}
513        ///             _ => unreachable!(),
514        ///         }
515        ///     }
516        ///
517        ///     // ...
518        /// }
519        /// ```
520        pub fn bytes_source_read(source: BytesSource, buffer_ptr: *mut u8, buffer_len_ptr: *mut usize) -> i16;
521
522        /// Logs at `level` a `message` message occurring in `filename:line_number`
523        /// with `target` being the module path at the `log!` invocation site.
524        ///
525        /// These various pointers are interpreted lossily as UTF-8 strings with a corresponding `_len`.
526        ///
527        /// The `target` and `filename` pointers are ignored by passing `NULL`.
528        /// The line number is ignored if `line_number == u32::MAX`.
529        ///
530        /// No message is logged if
531        /// - `target != NULL && target + target_len > u64::MAX`
532        /// - `filename != NULL && filename + filename_len > u64::MAX`
533        /// - `message + message_len > u64::MAX`
534        ///
535        /// # Traps
536        ///
537        /// Traps if:
538        /// - `target` is not NULL and `target_ptr[..target_len]` is not in bounds of WASM memory.
539        /// - `filename` is not NULL and `filename_ptr[..filename_len]` is not in bounds of WASM memory.
540        /// - `message` is not NULL and `message_ptr[..message_len]` is not in bounds of WASM memory.
541        ///
542        /// [target]: https://docs.rs/log/latest/log/struct.Record.html#method.target
543        pub fn console_log(
544            level: u8,
545            target_ptr: *const u8,
546            target_len: usize,
547            filename_ptr: *const u8,
548            filename_len: usize,
549            line_number: u32,
550            message_ptr: *const u8,
551            message_len: usize,
552        );
553
554        /// Begins a timing span with `name = name_ptr[..name_len]`.
555        ///
556        /// When the returned `ConsoleTimerId` is passed to [`console_timer_end`],
557        /// the duration between the calls will be printed to the module's logs.
558        ///
559        /// The `name` is interpreted lossily as UTF-8.
560        ///
561        /// # Traps
562        ///
563        /// Traps if:
564        /// - `name_ptr` is NULL or `name` is not in bounds of WASM memory.
565        pub fn console_timer_start(name_ptr: *const u8, name_len: usize) -> u32;
566
567        /// End a timing span.
568        ///
569        /// The `timer_id` must be the result of a call to `console_timer_start`.
570        /// The duration between the two calls will be computed and printed to the module's logs.
571        /// Once `console_timer_end` is called on `id: ConsoleTimerId`, the `id` is invalid.
572        /// That is, `console_timer_end(id)` the second time will yield `NO_SUCH_CONSOLE_TIMER`.
573        ///
574        /// Note that the host is free to reuse allocations in a pool,
575        /// destroying the handle logically does not entail that memory is necessarily reclaimed.
576        ///
577        /// # Errors
578        ///
579        /// Returns an error:
580        /// - `NO_SUCH_CONSOLE_TIMER`, when `timer_id` does not exist.
581        pub fn console_timer_end(timer_id: u32) -> u16;
582
583        /// Writes the identity of the module into `out = out_ptr[..32]`.
584        ///
585        /// # Traps
586        ///
587        /// Traps if:
588        ///
589        /// - `out_ptr` is NULL or `out` is not in bounds of WASM memory.
590        pub fn identity(out_ptr: *mut u8);
591    }
592
593    // See comment on previous `extern "C"` block re: ABI version.
594    #[link(wasm_import_module = "spacetime_10.1")]
595    extern "C" {
596        /// Read the remaining length of a [`BytesSource`] and write it to `out`.
597        ///
598        /// Note that the host automatically frees byte sources which are exhausted.
599        /// Such sources are invalid, and this method will return an error when passed one.
600        /// Callers of [`bytes_source_read`] should check for a return of -1
601        /// before invoking this function on the same `source`.
602        ///
603        /// Also note that the special [`BytesSource::INVALID`] (zero) is always invalid.
604        /// Callers should check for that value before invoking this function.
605        ///
606        /// # Traps
607        ///
608        /// Traps if:
609        ///
610        /// - `out` is NULL or `out` is not in bounds of WASM memory.
611        ///
612        /// # Errors
613        ///
614        /// Returns an error:
615        ///
616        /// - `NO_SUCH_BYTES`, when `source` is not a valid bytes source.
617        ///
618        /// If this function returns an error, `out` is not written.
619        pub fn bytes_source_remaining_length(source: BytesSource, out: *mut u32) -> i16;
620    }
621
622    // See comment on previous `extern "C"` block re: ABI version.
623    #[link(wasm_import_module = "spacetime_10.2")]
624    extern "C" {
625        /// Finds the JWT payload associated with `connection_id`.
626        /// A `[ByteSourceId]` for the payload will be written to `target_ptr`.
627        /// If nothing is found for the connection, `[ByteSourceId::INVALID]` (zero) is written to `target_ptr`.
628        ///
629        /// This must be called inside a transaction (because it reads from a system table).
630        ///
631        /// # Errors
632        ///
633        /// Returns an error:
634        ///
635        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
636        ///
637        /// # Traps
638        ///
639        /// Traps if:
640        ///
641        /// - `connection_id` does not point to a valid little-endian `ConnectionId`.
642        /// - `target_ptr` is NULL or `target_ptr[..size_of::<u32>()]` is not in bounds of WASM memory.
643        ///  - The `ByteSourceId` to be written to `target_ptr` would overflow [`u32::MAX`].
644        pub fn get_jwt(connection_id_ptr: *const u8, bytes_source_id: *mut BytesSource) -> u16;
645    }
646
647    #[cfg(feature = "unstable")]
648    #[link(wasm_import_module = "spacetime_10.3")]
649    extern "C" {
650        /// Suspends execution of this WASM instance until approximately `wake_at_micros_since_unix_epoch`.
651        ///
652        /// Returns immediately if `wake_at_micros_since_unix_epoch` is in the past.
653        ///
654        /// Upon resuming, returns the current timestamp as microseconds since the Unix epoch.
655        ///
656        /// Not particularly useful, except for testing SpacetimeDB internals related to suspending procedure execution.
657        /// # Traps
658        ///
659        /// Traps if:
660        ///
661        /// - The calling WASM instance is holding open a transaction.
662        /// - The calling WASM instance is not executing a procedure.
663        // TODO(procedure-sleep-until): remove this
664        pub fn procedure_sleep_until(wake_at_micros_since_unix_epoch: i64) -> i64;
665
666        /// Starts a mutable transaction,
667        /// suspending execution of this WASM instance until
668        /// a mutable transaction lock is aquired.
669        ///
670        /// Upon resuming, returns `0` on success,
671        /// enabling further calls that require a pending transaction,
672        /// or an error code otherwise.
673        ///
674        /// # Traps
675        ///
676        /// Traps if:
677        /// - `out` is NULL or `out[..size_of::<i64>()]` is not in bounds of WASM memory.
678        ///
679        /// # Errors
680        ///
681        /// Returns an error:
682        ///
683        /// - `WOULD_BLOCK_TRANSACTION`, if there's already an ongoing transaction.
684        pub fn procedure_start_mut_tx(out: *mut i64) -> u16;
685
686        /// Commits a mutable transaction,
687        /// suspending execution of this WASM instance until
688        /// the transaction has been committed
689        /// and subscription queries have been run and broadcast.
690        ///
691        /// Upon resuming, returns `0` on success, or an error code otherwise.
692        ///
693        /// # Traps
694        ///
695        /// This function does not trap.
696        ///
697        /// # Errors
698        ///
699        /// Returns an error:
700        ///
701        /// - `TRANSACTION_NOT_ANONYMOUS`,
702        ///   if the transaction was not started in [`procedure_start_mut_tx`].
703        ///   This can happen if this syscall is erroneously called by a reducer.
704        ///   The code `NOT_IN_TRANSACTION` does not happen,
705        ///   as it is subsumed by `TRANSACTION_NOT_ANONYMOUS`.
706        /// - `TRANSACTION_IS_READ_ONLY`, if the pending transaction is read-only.
707        ///   This currently does not happen as anonymous read transactions
708        ///   are not exposed to modules.
709        pub fn procedure_commit_mut_tx() -> u16;
710
711        /// Aborts a mutable transaction,
712        /// suspending execution of this WASM instance until
713        /// the transaction has been rolled back.
714        ///
715        /// Upon resuming, returns `0` on success, or an error code otherwise.
716        ///
717        /// # Traps
718        ///
719        /// This function does not trap.
720        ///
721        /// # Errors
722        ///
723        /// Returns an error:
724        ///
725        /// - `TRANSACTION_NOT_ANONYMOUS`,
726        ///   if the transaction was not started in [`procedure_start_mut_tx`].
727        ///   This can happen if this syscall is erroneously called by a reducer.
728        ///   The code `NOT_IN_TRANSACTION` does not happen,
729        ///   as it is subsumed by `TRANSACTION_NOT_ANONYMOUS`.
730        /// - `TRANSACTION_IS_READ_ONLY`, if the pending transaction is read-only.
731        ///   This currently does not happen as anonymous read transactions
732        ///   are not exposed to modules.
733        pub fn procedure_abort_mut_tx() -> u16;
734
735        /// Perform an HTTP request as specified by the buffer `request_ptr[..request_len]`,
736        /// suspending execution until the request is complete,
737        /// then return its response via a [`BytesSource`] written to `out`.
738        ///
739        /// `request_ptr[..request_len]` should store a BSATN-serialized `spacetimedb_lib::http::Request` object
740        /// containing the details of the request to be performed.
741        ///
742        /// If the request is successful, a [`BytesSource`] is written to `out`
743        /// containing a BSATN-encoded `spacetimedb_lib::http::Response` object.
744        /// "Successful" in this context includes any connection which results in any HTTP status code,
745        /// regardless of the specified meaning of that code.
746        ///
747        /// # Errors
748        ///
749        /// Returns an error:
750        ///
751        /// - `WOULD_BLOCK_TRANSACTION` if there is currently a transaction open.
752        ///   In this case, `out` is not written.
753        /// - `BSATN_DECODE_ERROR` if `request_ptr[..request_len]` does not contain
754        ///   a valid BSATN-serialized `spacetimedb_lib::http::Request` object.
755        ///   In this case, `out` is not written.
756        /// - `HTTP_ERROR` if an error occurs while executing the HTTP request.
757        ///   In this case, a [`BytesSource`] is written to `out`
758        ///   containing a BSATN-encoded `spacetimedb_lib::http::Error` object.
759        ///
760        /// # Traps
761        ///
762        /// Traps if:
763        ///
764        /// - `request_ptr` is NULL or `request_ptr[..request_len]` is not in bounds of WASM memory.
765        /// - `out` is NULL or `out[..size_of::<RowIter>()]` is not in bounds of WASM memory.
766        /// - `request_ptr[..request_len]` does not contain a valid BSATN-serialized `spacetimedb_lib::http::Request` object.
767        #[cfg(feature = "unstable")]
768        pub fn procedure_http_request(
769            request_ptr: *const u8,
770            request_len: u32,
771            body_ptr: *const u8,
772            body_len: u32,
773            out: *mut [BytesSource; 2],
774        ) -> u16;
775    }
776
777    #[link(wasm_import_module = "spacetime_10.4")]
778    extern "C" {
779        /// Finds all rows in the index identified by `index_id`,
780        /// according to `point = point_ptr[..point_len]` in WASM memory.
781        ///
782        /// The index itself has a schema/type.
783        /// Matching defined by first BSATN-decoding `point` to that `AlgebraicType`
784        /// and then comparing the decoded `point` to the keys in the index
785        /// using `Ord for AlgebraicValue`.
786        /// to the keys in the index.
787        /// The `point` is BSATN-decoded to that `AlgebraicType`.
788        /// A match happens when `Ordering::Equal` is returned from `fn cmp`.
789        /// This occurs exactly when the row's BSATN-encoding
790        /// is equal to the encoding of the `AlgebraicValue`.
791        ///
792        /// This ABI is not limited to single column indices.
793        /// Multi-column indices can be queried by providing
794        /// a BSATN-encoded `ProductValue`
795        /// that is typed at the `ProductType` of the index.
796        ///
797        /// The relevant table for the index is found implicitly via the `index_id`,
798        /// which is unique for the module.
799        ///
800        /// On success, the iterator handle is written to the `out` pointer.
801        /// This handle can be advanced by [`row_iter_bsatn_advance`].
802        ///
803        /// # Traps
804        ///
805        /// Traps if:
806        /// - `point_ptr` is NULL or `point` is not in bounds of WASM memory.
807        /// - `out` is NULL or `out[..size_of::<RowIter>()]` is not in bounds of WASM memory.
808        ///
809        /// # Errors
810        ///
811        /// Returns an error:
812        ///
813        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
814        /// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
815        /// - `WRONG_INDEX_ALGO` if the index is not a range-scan compatible index.
816        /// - `BSATN_DECODE_ERROR`, when `point` cannot be decoded to an `AlgebraicValue`
817        ///   typed at the index's key type (`AlgebraicType`).
818        pub fn datastore_index_scan_point_bsatn(
819            index_id: IndexId,
820            point_ptr: *const u8, // AlgebraicValue
821            point_len: usize,
822            out: *mut RowIter,
823        ) -> u16;
824
825        /// Deletes all rows found in the index identified by `index_id`,
826        /// according to `point = point_ptr[..point_len]` in WASM memory.
827        ///
828        /// This syscall will delete all the rows found by
829        /// [`datastore_index_scan_point_bsatn`] with the same arguments passed.
830        /// See `datastore_index_scan_point_bsatn` for details.
831        ///
832        /// The number of rows deleted is written to the WASM pointer `out`.
833        ///
834        /// # Traps
835        ///
836        /// Traps if:
837        /// - `point_ptr` is NULL or `point` is not in bounds of WASM memory.
838        /// - `out` is NULL or `out[..size_of::<u32>()]` is not in bounds of WASM memory.
839        ///
840        /// # Errors
841        ///
842        /// Returns an error:
843        ///
844        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
845        /// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
846        /// - `WRONG_INDEX_ALGO` if the index is not a range-compatible index.
847        /// - `BSATN_DECODE_ERROR`, when `point` cannot be decoded to an `AlgebraicValue`
848        ///   typed at the index's key type (`AlgebraicType`).
849        pub fn datastore_delete_by_index_scan_point_bsatn(
850            index_id: IndexId,
851            point_ptr: *const u8, // AlgebraicValue
852            point_len: usize,
853            out: *mut u32,
854        ) -> u16;
855    }
856
857    /// What strategy does the database index use?
858    ///
859    /// See also: <https://www.postgresql.org/docs/current/sql-createindex.html>
860    #[repr(u8)]
861    #[non_exhaustive]
862    pub enum IndexType {
863        /// Indexing works by putting the index key into a b-tree.
864        BTree = 0,
865        /// Indexing works by hashing the index key.
866        Hash = 1,
867    }
868
869    /// The error log level. See [`console_log`].
870    pub const LOG_LEVEL_ERROR: u8 = 0;
871    /// The warn log level. See [`console_log`].
872    pub const LOG_LEVEL_WARN: u8 = 1;
873    /// The info log level. See [`console_log`].
874    pub const LOG_LEVEL_INFO: u8 = 2;
875    /// The debug log level. See [`console_log`].
876    pub const LOG_LEVEL_DEBUG: u8 = 3;
877    /// The trace log level. See [`console_log`].
878    pub const LOG_LEVEL_TRACE: u8 = 4;
879    /// The panic log level. See [`console_log`].
880    ///
881    /// A panic level is emitted just before a fatal error causes the WASM module to trap.
882    pub const LOG_LEVEL_PANIC: u8 = 101;
883
884    /// A handle into a buffer of bytes in the host environment that can be read from.
885    ///
886    /// Used for transporting bytes from host to WASM linear memory.
887    #[derive(PartialEq, Eq, Copy, Clone)]
888    #[repr(transparent)]
889    pub struct BytesSource(u32);
890
891    impl BytesSource {
892        /// An invalid handle, used e.g., when the reducer arguments were empty.
893        pub const INVALID: Self = Self(0);
894    }
895
896    /// A handle into a buffer of bytes in the host environment that can be written to.
897    ///
898    /// Used for transporting bytes from WASM linear memory to host.
899    #[derive(PartialEq, Eq, Copy, Clone)]
900    #[repr(transparent)]
901    pub struct BytesSink(u32);
902
903    /// Represents table iterators.
904    #[derive(PartialEq, Eq, Copy, Clone)]
905    #[repr(transparent)]
906    pub struct RowIter(u32);
907
908    impl RowIter {
909        /// An invalid handle, used e.g., when the iterator has been exhausted.
910        pub const INVALID: Self = Self(0);
911    }
912
913    #[cfg(any())]
914    mod module_exports {
915        type Encoded<T> = Buffer;
916        type Identity = Encoded<[u8; 32]>;
917        /// Microseconds since the unix epoch
918        type Timestamp = u64;
919        /// Buffer::INVALID => Ok(()); else errmsg => Err(errmsg)
920        type Result = Buffer;
921        extern "C" {
922            /// All functions prefixed with `__preinit__` are run first in alphabetical order.
923            /// For those it's recommended to use /etc/xxxx.d conventions of like `__preinit__20_do_thing`:
924            /// <https://man7.org/linux/man-pages/man5/sysctl.d.5.html#CONFIGURATION_DIRECTORIES_AND_PRECEDENCE>
925            fn __preinit__XX_XXXX();
926            /// Optional. Run after `__preinit__`; can return an error. Intended for dynamic languages; this
927            /// would be where you would initialize the interepreter and load the user module into it.
928            fn __setup__() -> Result;
929            /// Required. Runs after `__setup__`; returns all the exports for the module.
930            fn __describe_module__() -> Encoded<ModuleDef>;
931            /// Required. id is an index into the `ModuleDef.reducers` returned from `__describe_module__`.
932            /// args is a bsatn-encoded product value defined by the schema at `reducers[id]`.
933            fn __call_reducer__(
934                id: usize,
935                sender_0: u64,
936                sender_1: u64,
937                sender_2: u64,
938                sender_3: u64,
939                conn_id_0: u64,
940                conn_id_1: u64,
941                timestamp: u64,
942                args: Buffer,
943            ) -> Result;
944            /// Currently unused?
945            fn __migrate_database__XXXX(sender: Identity, timestamp: Timestamp, something: Buffer) -> Result;
946        }
947    }
948}
949
950/// Error values used in the safe bindings API.
951#[derive(Copy, Clone, PartialEq, Eq)]
952#[repr(transparent)]
953pub struct Errno(NonZeroU16);
954
955// once Error gets exposed from core this crate can be no_std again
956impl std::error::Error for Errno {}
957
958pub type Result<T, E = Errno> = core::result::Result<T, E>;
959
960macro_rules! def_errno {
961    ($($err_name:ident($errno:literal, $errmsg:literal),)*) => {
962        impl Errno {
963            $(#[doc = $errmsg] pub const $err_name: Errno = Errno(errno::$err_name);)*
964        }
965    };
966}
967errnos!(def_errno);
968
969impl Errno {
970    /// Returns a description of the errno value, if any.
971    pub const fn message(self) -> Option<&'static str> {
972        errno::strerror(self.0)
973    }
974
975    /// Converts the given `code` to an error number in `Errno`'s representation.
976    #[inline]
977    pub const fn from_code(code: u16) -> Option<Self> {
978        match NonZeroU16::new(code) {
979            Some(code) => Some(Errno(code)),
980            None => None,
981        }
982    }
983
984    /// Converts this `errno` into a primitive error code.
985    #[inline]
986    pub const fn code(self) -> u16 {
987        self.0.get()
988    }
989}
990
991impl fmt::Debug for Errno {
992    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
993        let mut fmt = f.debug_struct("Errno");
994        fmt.field("code", &self.code());
995        if let Some(msg) = self.message() {
996            fmt.field("message", &msg);
997        }
998        fmt.finish()
999    }
1000}
1001
1002impl fmt::Display for Errno {
1003    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1004        let message = self.message().unwrap_or("Unknown error");
1005        write!(f, "{message} (error {})", self.code())
1006    }
1007}
1008
1009/// Convert the status value `x` into a result.
1010/// When `x = 0`, we have a success status.
1011fn cvt(x: u16) -> Result<()> {
1012    match Errno::from_code(x) {
1013        None => Ok(()),
1014        Some(err) => Err(err),
1015    }
1016}
1017
1018/// Runs the given function `f` provided with an uninitialized `out` pointer.
1019///
1020/// Assuming the call to `f` succeeds (`Ok(_)`), the `out` pointer's value is returned.
1021///
1022/// # Safety
1023///
1024/// This function is safe to call, if and only if,
1025/// - The function `f` writes a safe and valid `T` to the `out` pointer.
1026///   It's not required to write to `out` when `f(out)` returns an error code.
1027/// - The function `f` never reads a safe and valid `T` from the `out` pointer
1028///   before writing a safe and valid `T` to it.
1029#[inline]
1030unsafe fn call<T: Copy>(f: impl FnOnce(*mut T) -> u16) -> Result<T> {
1031    let mut out = MaybeUninit::uninit();
1032    let f_code = f(out.as_mut_ptr());
1033    cvt(f_code)?;
1034    Ok(out.assume_init())
1035}
1036
1037/// Runs the given function `f`.
1038///
1039/// Assuming the call to `f` returns 0, `Ok(())` is returned,
1040/// and otherwise `Err(err)` is returned.
1041#[inline]
1042#[cfg(feature = "unstable")]
1043fn call_no_ret(f: impl FnOnce() -> u16) -> Result<()> {
1044    let f_code = f();
1045    cvt(f_code)?;
1046    Ok(())
1047}
1048
1049/// Queries the `table_id` associated with the given (table) `name`.
1050///
1051/// The table id is returned.
1052///
1053/// # Errors
1054///
1055/// Returns an error:
1056///
1057/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
1058/// - `NO_SUCH_TABLE`, when `name` is not the name of a table.
1059#[inline]
1060pub fn table_id_from_name(name: &str) -> Result<TableId> {
1061    unsafe { call(|out| raw::table_id_from_name(name.as_ptr(), name.len(), out)) }
1062}
1063
1064/// Queries the `index_id` associated with the given (index) `name`.
1065///
1066/// The index id is returned.
1067///
1068/// # Errors
1069///
1070/// Returns an error:
1071///
1072/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
1073/// - `NO_SUCH_INDEX`, when `name` is not the name of an index.
1074#[inline]
1075pub fn index_id_from_name(name: &str) -> Result<IndexId> {
1076    unsafe { call(|out| raw::index_id_from_name(name.as_ptr(), name.len(), out)) }
1077}
1078
1079/// Returns the number of rows currently in table identified by `table_id`.
1080///
1081/// # Errors
1082///
1083/// Returns an error:
1084///
1085/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
1086/// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.
1087#[inline]
1088pub fn datastore_table_row_count(table_id: TableId) -> Result<u64> {
1089    unsafe { call(|out| raw::datastore_table_row_count(table_id, out)) }
1090}
1091
1092/// Inserts a row into the table identified by `table_id`,
1093/// where the row is a BSATN-encoded `ProductValue`
1094/// matching the table's `ProductType` row-schema.
1095///
1096/// The `row` is `&mut` due to auto-incrementing columns.
1097/// So `row` is written to with the inserted row re-encoded.
1098///
1099/// Returns an error if
1100/// - a table with the provided `table_id` doesn't exist
1101/// - there were unique constraint violations
1102/// - `row` doesn't decode from BSATN to a `ProductValue`
1103///   according to the `ProductType` that the table's schema specifies.
1104#[inline]
1105pub fn datastore_insert_bsatn(table_id: TableId, row: &mut [u8]) -> Result<&[u8]> {
1106    let row_ptr = row.as_mut_ptr();
1107    let row_len = &mut row.len();
1108    cvt(unsafe { raw::datastore_insert_bsatn(table_id, row_ptr, row_len) }).map(|()| &row[..*row_len])
1109}
1110
1111/// Updates a row into the table identified by `table_id`,
1112/// where the row is a BSATN-encoded `ProductValue`
1113/// matching the table's `ProductType` row-schema.
1114///
1115/// The row to update is found by projecting `row`
1116/// to the type of the *unique* index identified by `index_id`.
1117/// If no row is found, `row` is inserted.
1118///
1119/// The `row` is `&mut` due to auto-incrementing columns.
1120/// So `row` is written to with the updated row re-encoded.
1121///
1122/// Returns an error if
1123/// - a table with the provided `table_id` doesn't exist
1124/// - an index with the provided `index_id` doesn't exist or if the index was not unique.
1125/// - there were unique constraint violations
1126/// - `row` doesn't decode from BSATN to a `ProductValue`
1127///   according to the `ProductType` that the table's schema specifies
1128///   or if `row` cannot project to the index's type.
1129/// - the row was not found
1130#[inline]
1131pub fn datastore_update_bsatn(table_id: TableId, index_id: IndexId, row: &mut [u8]) -> Result<&[u8]> {
1132    let row_ptr = row.as_mut_ptr();
1133    let row_len = &mut row.len();
1134    cvt(unsafe { raw::datastore_update_bsatn(table_id, index_id, row_ptr, row_len) }).map(|()| &row[..*row_len])
1135}
1136
1137/// Deletes those rows, in the table identified by `table_id`,
1138/// that match any row in the byte string `relation`.
1139///
1140/// Matching is defined by first BSATN-decoding
1141/// the byte string pointed to at by `relation` to a `Vec<ProductValue>`
1142/// according to the row schema of the table
1143/// and then using `Ord for AlgebraicValue`.
1144/// A match happens when `Ordering::Equal` is returned from `fn cmp`.
1145/// This occurs exactly when the row's BSATN-encoding is equal to the encoding of the `ProductValue`.
1146///
1147/// The number of rows deleted is returned.
1148///
1149/// # Errors
1150///
1151/// Returns an error:
1152///
1153/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
1154/// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.
1155/// - `BSATN_DECODE_ERROR`, when `rel` cannot be decoded to `Vec<ProductValue>`
1156///   where each `ProductValue` is typed at the `ProductType` the table's schema specifies.
1157#[inline]
1158pub fn datastore_delete_all_by_eq_bsatn(table_id: TableId, relation: &[u8]) -> Result<u32> {
1159    unsafe { call(|out| raw::datastore_delete_all_by_eq_bsatn(table_id, relation.as_ptr(), relation.len(), out)) }
1160}
1161
1162/// Starts iteration on each row, as BSATN-encoded, of a table identified by `table_id`.
1163/// Returns iterator handle is written to the `out` pointer.
1164/// This handle can be advanced by [`RowIter::read`].
1165///
1166/// # Errors
1167///
1168/// Returns an error:
1169///
1170/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
1171/// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.
1172pub fn datastore_table_scan_bsatn(table_id: TableId) -> Result<RowIter> {
1173    let raw = unsafe { call(|out| raw::datastore_table_scan_bsatn(table_id, out))? };
1174    Ok(RowIter { raw })
1175}
1176
1177/// Finds all rows in the index identified by `index_id`,
1178/// according to the `point.
1179///
1180/// The index itself has a schema/type.
1181/// Matching defined by first BSATN-decoding `point` to that `AlgebraicType`
1182/// and then comparing the decoded `point` to the keys in the index
1183/// using `Ord for AlgebraicValue`.
1184/// to the keys in the index.
1185/// The `point` is BSATN-decoded to that `AlgebraicType`.
1186/// A match happens when `Ordering::Equal` is returned from `fn cmp`.
1187/// This occurs exactly when the row's BSATN-encoding
1188/// is equal to the encoding of the `AlgebraicValue`.
1189///
1190/// This ABI is not limited to single column indices.
1191/// Multi-column indices can be queried by providing
1192/// a BSATN-encoded `ProductValue`
1193/// that is typed at the `ProductType` of the index.
1194///
1195/// The relevant table for the index is found implicitly via the `index_id`,
1196/// which is unique for the module.
1197///
1198/// On success, the iterator handle is written to the `out` pointer.
1199/// This handle can be advanced by [`RowIter::read`].
1200///
1201/// # Errors
1202///
1203/// Returns an error:
1204///
1205/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
1206/// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
1207/// - `WRONG_INDEX_ALGO` if the index is not a range-compatible index.
1208/// - `BSATN_DECODE_ERROR`, when `point` cannot be decoded to an `AlgebraicValue`
1209///   typed at the index's key type (`AlgebraicType`).
1210pub fn datastore_index_scan_point_bsatn(index_id: IndexId, point: &[u8]) -> Result<RowIter> {
1211    let raw = unsafe { call(|out| raw::datastore_index_scan_point_bsatn(index_id, point.as_ptr(), point.len(), out))? };
1212    Ok(RowIter { raw })
1213}
1214
1215/// Finds all rows in the index identified by `index_id`,
1216/// according to the `prefix`, `rstart`, and `rend`.
1217///
1218/// The index itself has a schema/type.
1219/// The `prefix` is decoded to the initial `prefix_elems` `AlgebraicType`s
1220/// whereas `rstart` and `rend` are decoded to the `prefix_elems + 1` `AlgebraicType`
1221/// where the `AlgebraicValue`s are wrapped in `Bound`.
1222/// That is, `rstart, rend` are BSATN-encoded `Bound<AlgebraicValue>`s.
1223///
1224/// Matching is then defined by equating `prefix`
1225/// to the initial `prefix_elems` columns of the index
1226/// and then imposing `rstart` as the starting bound
1227/// and `rend` as the ending bound on the `prefix_elems + 1` column of the index.
1228/// Remaining columns of the index are then unbounded.
1229/// Note that the `prefix` in this case can be empty (`prefix_elems = 0`),
1230/// in which case this becomes a ranged index scan on a single-col index
1231/// or even a full table scan if `rstart` and `rend` are both unbounded.
1232///
1233/// The relevant table for the index is found implicitly via the `index_id`,
1234/// which is unique for the module.
1235///
1236/// On success, the iterator handle is written to the `out` pointer.
1237/// This handle can be advanced by [`RowIter::read`].
1238///
1239/// # Non-obvious queries
1240///
1241/// For an index on columns `[a, b, c]`:
1242///
1243/// - `a = x, b = y` is encoded as a prefix `[x, y]`
1244///   and a range `Range::Unbounded`,
1245///   or as a  prefix `[x]` and a range `rstart = rend = Range::Inclusive(y)`.
1246/// - `a = x, b = y, c = z` is encoded as a prefix `[x, y]`
1247///   and a  range `rstart = rend = Range::Inclusive(z)`.
1248/// - A sorted full scan is encoded as an empty prefix
1249///   and a range `Range::Unbounded`.
1250///
1251/// # Errors
1252///
1253/// Returns an error:
1254///
1255/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
1256/// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
1257/// - `WRONG_INDEX_ALGO` if the index is not a range-compatible index.
1258/// - `BSATN_DECODE_ERROR`, when `prefix` cannot be decoded to
1259///   a `prefix_elems` number of `AlgebraicValue`
1260///   typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.
1261///   Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`
1262///   where the inner `AlgebraicValue`s are
1263///   typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.
1264pub fn datastore_index_scan_range_bsatn(
1265    index_id: IndexId,
1266    prefix: &[u8],
1267    prefix_elems: ColId,
1268    rstart: &[u8],
1269    rend: &[u8],
1270) -> Result<RowIter> {
1271    let raw = unsafe {
1272        call(|out| {
1273            raw::datastore_index_scan_range_bsatn(
1274                index_id,
1275                prefix.as_ptr(),
1276                prefix.len(),
1277                prefix_elems,
1278                rstart.as_ptr(),
1279                rstart.len(),
1280                rend.as_ptr(),
1281                rend.len(),
1282                out,
1283            )
1284        })?
1285    };
1286    Ok(RowIter { raw })
1287}
1288
1289/// Deletes all rows found in the index identified by `index_id`,
1290/// according to the `point.
1291///
1292/// This syscall will delete all the rows found by
1293/// [`datastore_index_scan_point_bsatn`] with the same arguments passed.
1294/// See `datastore_index_scan_point_bsatn` for details.
1295///
1296/// The number of rows deleted is returned on success.
1297///
1298/// # Errors
1299///
1300/// Returns an error:
1301///
1302/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
1303/// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
1304/// - `WRONG_INDEX_ALGO` if the index is not a range-compatible index.
1305/// - `BSATN_DECODE_ERROR`, when `point` cannot be decoded to an `AlgebraicValue`
1306///   typed at the index's key type (`AlgebraicType`).
1307pub fn datastore_delete_by_index_scan_point_bsatn(index_id: IndexId, point: &[u8]) -> Result<u32> {
1308    unsafe { call(|out| raw::datastore_delete_by_index_scan_point_bsatn(index_id, point.as_ptr(), point.len(), out)) }
1309}
1310
1311/// Deletes all rows found in the index identified by `index_id`,
1312/// according to the `prefix`, `rstart`, and `rend`.
1313///
1314/// This syscall will delete all the rows found by
1315/// [`datastore_index_scan_range_bsatn`] with the same arguments passed,
1316/// including `prefix_elems`.
1317/// See `datastore_index_scan_range_bsatn` for details.
1318///
1319/// The number of rows deleted is returned on success.
1320///
1321/// # Errors
1322///
1323/// Returns an error:
1324///
1325/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
1326/// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
1327/// - `WRONG_INDEX_ALGO` if the index is not a range-compatible index.
1328/// - `BSATN_DECODE_ERROR`, when `prefix` cannot be decoded to
1329///   a `prefix_elems` number of `AlgebraicValue`
1330///   typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.
1331///   Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`
1332///   where the inner `AlgebraicValue`s are
1333///   typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.
1334pub fn datastore_delete_by_index_scan_range_bsatn(
1335    index_id: IndexId,
1336    prefix: &[u8],
1337    prefix_elems: ColId,
1338    rstart: &[u8],
1339    rend: &[u8],
1340) -> Result<u32> {
1341    unsafe {
1342        call(|out| {
1343            raw::datastore_delete_by_index_scan_range_bsatn(
1344                index_id,
1345                prefix.as_ptr(),
1346                prefix.len(),
1347                prefix_elems,
1348                rstart.as_ptr(),
1349                rstart.len(),
1350                rend.as_ptr(),
1351                rend.len(),
1352                out,
1353            )
1354        })
1355    }
1356}
1357
1358/// A log level that can be used in `console_log`.
1359/// The variants are convertible into a raw `u8` log level.
1360#[repr(u8)]
1361pub enum LogLevel {
1362    /// The error log level. See [`console_log`].
1363    Error = raw::LOG_LEVEL_ERROR,
1364    /// The warn log level. See [`console_log`].
1365    Warn = raw::LOG_LEVEL_WARN,
1366    /// The info log level. See [`console_log`].
1367    Info = raw::LOG_LEVEL_INFO,
1368    /// The debug log level. See [`console_log`].
1369    Debug = raw::LOG_LEVEL_DEBUG,
1370    /// The trace log level. See [`console_log`].
1371    Trace = raw::LOG_LEVEL_TRACE,
1372    /// The panic log level. See [`console_log`].
1373    ///
1374    /// A panic level is emitted just before a fatal error causes the WASM module to trap.
1375    Panic = raw::LOG_LEVEL_PANIC,
1376}
1377
1378/// Log at `level` a `text` message occurring in `filename:line_number`
1379/// with [`target`] being the module path at the `log!` invocation site.
1380///
1381/// [`target`]: https://docs.rs/log/latest/log/struct.Record.html#method.target
1382#[inline]
1383pub fn console_log(
1384    level: LogLevel,
1385    target: Option<&str>,
1386    filename: Option<&str>,
1387    line_number: Option<u32>,
1388    text: &str,
1389) {
1390    let opt_ptr = |b: Option<&str>| b.map_or(ptr::null(), |b| b.as_ptr());
1391    let opt_len = |b: Option<&str>| b.map_or(0, |b| b.len());
1392    unsafe {
1393        raw::console_log(
1394            level as u8,
1395            opt_ptr(target),
1396            opt_len(target),
1397            opt_ptr(filename),
1398            opt_len(filename),
1399            line_number.unwrap_or(u32::MAX),
1400            text.as_ptr(),
1401            text.len(),
1402        )
1403    }
1404}
1405
1406/// Schedule a reducer to be called asynchronously, nonatomically, and immediately
1407/// on a best-effort basis.
1408///
1409/// The reducer is assigned `name` and is provided `args` as its argument.
1410#[cfg(feature = "unstable")]
1411#[inline]
1412pub fn volatile_nonatomic_schedule_immediate(name: &str, args: &[u8]) {
1413    unsafe { raw::volatile_nonatomic_schedule_immediate(name.as_ptr(), name.len(), args.as_ptr(), args.len()) }
1414}
1415
1416/// Read the current module's identity, as a little-endian byte array.
1417///
1418/// Doesn't return a proper typed `Identity` because this crate doesn't depend on `spacetimedb_lib`.
1419#[inline]
1420pub fn identity() -> [u8; 32] {
1421    let mut buf = [0u8; 32];
1422    unsafe {
1423        raw::identity(buf.as_mut_ptr());
1424    }
1425    buf
1426}
1427
1428/// Finds the JWT payload associated with `connection_id`.
1429/// If nothing is found for the connection, this returns None.
1430/// If a payload is found, this will return a valid [`raw::BytesSource`].
1431///
1432/// This must be called inside a transaction (because it reads from a system table).
1433///
1434/// # Errors
1435///
1436/// This panics on any error. You can see details about errors in [`raw::get_jwt`].
1437#[inline]
1438pub fn get_jwt(connection_id: [u8; 16]) -> Option<raw::BytesSource> {
1439    let source = unsafe {
1440        call(|out| raw::get_jwt(connection_id.as_ptr(), out))
1441            .unwrap_or_else(|errno: Errno| panic!("Error getting jwt: {errno}"))
1442    };
1443
1444    if source == raw::BytesSource::INVALID {
1445        None // No JWT found.
1446    } else {
1447        Some(source)
1448    }
1449}
1450
1451pub struct RowIter {
1452    raw: raw::RowIter,
1453}
1454
1455impl RowIter {
1456    /// Read some number of BSATN-encoded rows into the provided buffer.
1457    ///
1458    /// Returns the number of new bytes added to the end of the buffer.
1459    /// When the iterator has been exhausted,
1460    /// `self.is_exhausted()` will return `true`.
1461    pub fn read(&mut self, buf: &mut Vec<u8>) -> usize {
1462        loop {
1463            let buf_ptr = buf.spare_capacity_mut();
1464            let mut buf_len = buf_ptr.len();
1465            let ret = unsafe { raw::row_iter_bsatn_advance(self.raw, buf_ptr.as_mut_ptr().cast(), &mut buf_len) };
1466            if let -1 | 0 = ret {
1467                // SAFETY: `_row_iter_bsatn_advance` just wrote `buf_len` bytes into the end of `buf`.
1468                unsafe { buf.set_len(buf.len() + buf_len) };
1469            }
1470
1471            const TOO_SMALL: i16 = errno::BUFFER_TOO_SMALL.get() as i16;
1472            match ret {
1473                -1 => {
1474                    self.raw = raw::RowIter::INVALID;
1475                    return buf_len;
1476                }
1477                0 => return buf_len,
1478                TOO_SMALL => buf.reserve(buf_len),
1479                e => panic!("unexpected error from `_row_iter_bsatn_advance`: {e}"),
1480            }
1481        }
1482    }
1483
1484    /// Returns whether the iterator is exhausted or not.
1485    pub fn is_exhausted(&self) -> bool {
1486        self.raw == raw::RowIter::INVALID
1487    }
1488}
1489
1490impl Drop for RowIter {
1491    fn drop(&mut self) {
1492        // Avoid this syscall when `_row_iter_bsatn_advance` above
1493        // notifies us that the iterator is exhausted.
1494        if self.is_exhausted() {
1495            return;
1496        }
1497        unsafe {
1498            raw::row_iter_bsatn_close(self.raw);
1499        }
1500    }
1501}
1502
1503#[cfg(feature = "unstable")]
1504pub mod procedure {
1505    //! Side-effecting or asynchronous operations which only procedures are allowed to perform.
1506
1507    use super::{call, call_no_ret, raw, Result};
1508
1509    #[inline]
1510    pub fn sleep_until(wake_at_timestamp: i64) -> i64 {
1511        // Safety: Just calling an `extern "C"` function.
1512        // Nothing weird happening here.
1513        unsafe { raw::procedure_sleep_until(wake_at_timestamp) }
1514    }
1515
1516    /// Starts a mutable transaction,
1517    /// suspending execution of this WASM instance until
1518    /// a mutable transaction lock is aquired.
1519    ///
1520    /// Upon resuming, returns `Ok(timestamp)` on success,
1521    /// enabling further calls that require a pending transaction,
1522    /// or [`Errno`] otherwise.
1523    ///
1524    /// # Errors
1525    ///
1526    /// Returns an error:
1527    ///
1528    /// - `WOULD_BLOCK_TRANSACTION`, if there's already an ongoing transaction.
1529    #[inline]
1530    pub fn procedure_start_mut_tx() -> Result<i64> {
1531        unsafe { call(|out| raw::procedure_start_mut_tx(out)) }
1532    }
1533
1534    /// Commits a mutable transaction,
1535    /// suspending execution of this WASM instance until
1536    /// the transaction has been committed
1537    /// and subscription queries have been run and broadcast.
1538    ///
1539    /// Upon resuming, returns `Ok(()` on success, or an [`Errno`] otherwise.
1540    ///
1541    /// # Errors
1542    ///
1543    /// Returns an error:
1544    ///
1545    /// - `TRANSACTION_NOT_ANONYMOUS`,
1546    ///   if the transaction was not started in [`procedure_start_mut_tx`].
1547    ///   This can happen if this syscall is erroneously called by a reducer.
1548    ///   The code `NOT_IN_TRANSACTION` does not happen,
1549    ///   as it is subsumed by `TRANSACTION_NOT_ANONYMOUS`.
1550    /// - `TRANSACTION_IS_READ_ONLY`, if the pending transaction is read-only.
1551    ///   This currently does not happen as anonymous read transactions
1552    ///   are not exposed to modules.
1553    #[inline]
1554    pub fn procedure_commit_mut_tx() -> Result<()> {
1555        call_no_ret(|| unsafe { raw::procedure_commit_mut_tx() })
1556    }
1557
1558    /// Aborts a mutable transaction,
1559    /// suspending execution of this WASM instance until
1560    /// the transaction has been rolled back.
1561    ///
1562    /// Upon resuming, returns `Ok(())` on success, or an [`Errno`] otherwise.
1563    ///
1564    /// # Errors
1565    ///
1566    /// Returns an error:
1567    ///
1568    /// - `TRANSACTION_NOT_ANONYMOUS`,
1569    ///   if the transaction was not started in [`procedure_start_mut_tx`].
1570    ///   This can happen if this syscall is erroneously called by a reducer.
1571    ///   The code `NOT_IN_TRANSACTION` does not happen,
1572    ///   as it is subsumed by `TRANSACTION_NOT_ANONYMOUS`.
1573    /// - `TRANSACTION_IS_READ_ONLY`, if the pending transaction is read-only.
1574    ///   This currently does not happen as anonymous read transactions
1575    ///   are not exposed to modules.
1576    #[inline]
1577    pub fn procedure_abort_mut_tx() -> Result<()> {
1578        call_no_ret(|| unsafe { raw::procedure_abort_mut_tx() })
1579    }
1580
1581    #[inline]
1582    #[cfg(feature = "unstable")]
1583    /// Perform an HTTP request as specified by `http_request_bsatn`,
1584    /// suspending execution until the request is complete,
1585    /// then return its response or error.
1586    ///
1587    /// `http_request_bsatn` should be a BSATN-serialized `spacetimedb_lib::http::Request`.
1588    ///
1589    /// If the request completes successfully,
1590    /// this function returns `Ok(bytes)`, where `bytes` contains a BSATN-serialized `spacetimedb_lib::http::Response`.
1591    /// All HTTP response codes are treated as successful for these purposes;
1592    /// this method only returns an error if it is unable to produce any HTTP response whatsoever.
1593    /// In that case, this function returns `Err(bytes)`, where `bytes` contains a BSATN-serialized `spacetimedb_lib::http::Error`.
1594    pub fn http_request(
1595        http_request_bsatn: &[u8],
1596        body: &[u8],
1597    ) -> Result<(raw::BytesSource, raw::BytesSource), raw::BytesSource> {
1598        let mut out = [raw::BytesSource::INVALID; 2];
1599
1600        let res = unsafe {
1601            super::raw::procedure_http_request(
1602                http_request_bsatn.as_ptr(),
1603                http_request_bsatn.len() as u32,
1604                body.as_ptr(),
1605                body.len() as u32,
1606                &mut out as *mut [raw::BytesSource; 2],
1607            )
1608        };
1609
1610        match super::Errno::from_code(res) {
1611            // Success: `out` is a `spacetimedb_lib::http::Response`.
1612            None => Ok((out[0], out[1])),
1613            // HTTP_ERROR: `out` is a `spacetimedb_lib::http::Error`.
1614            Some(errno) if errno == super::Errno::HTTP_ERROR => Err(out[0]),
1615            Some(errno) => panic!("{errno}"),
1616        }
1617    }
1618}