Skip to main content

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    unsafe 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    unsafe 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    unsafe 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    unsafe 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        /// blocking until a mutable transaction lock is acquired.
668        ///
669        /// Returns `0` on success,
670        /// enabling further calls that require a pending transaction,
671        /// or an error code otherwise.
672        ///
673        /// # Traps
674        ///
675        /// Traps if:
676        /// - `out` is NULL or `out[..size_of::<i64>()]` is not in bounds of WASM memory.
677        ///
678        /// # Errors
679        ///
680        /// Returns an error:
681        ///
682        /// - `WOULD_BLOCK_TRANSACTION`, if there's already an ongoing transaction.
683        pub fn procedure_start_mut_tx(out: *mut i64) -> u16;
684
685        /// Commits a mutable transaction,
686        /// blocking until the transaction has been committed
687        /// and subscription queries have been run and broadcast.
688        ///
689        /// Once complete, it returns `0` on success, or an error code otherwise.
690        ///
691        /// # Traps
692        ///
693        /// This function does not trap.
694        ///
695        /// # Errors
696        ///
697        /// Returns an error:
698        ///
699        /// - `TRANSACTION_NOT_ANONYMOUS`,
700        ///   if the transaction was not started in [`procedure_start_mut_tx`].
701        ///   This can happen if this syscall is erroneously called by a reducer.
702        ///   The code `NOT_IN_TRANSACTION` does not happen,
703        ///   as it is subsumed by `TRANSACTION_NOT_ANONYMOUS`.
704        /// - `TRANSACTION_IS_READ_ONLY`, if the pending transaction is read-only.
705        ///   This currently does not happen as anonymous read transactions
706        ///   are not exposed to modules.
707        pub fn procedure_commit_mut_tx() -> u16;
708
709        /// Aborts a mutable transaction,
710        /// blocking until the transaction has been aborted.
711        ///
712        /// Returns `0` on success, or an error code otherwise.
713        ///
714        /// # Traps
715        ///
716        /// This function does not trap.
717        ///
718        /// # Errors
719        ///
720        /// Returns an error:
721        ///
722        /// - `TRANSACTION_NOT_ANONYMOUS`,
723        ///   if the transaction was not started in [`procedure_start_mut_tx`].
724        ///   This can happen if this syscall is erroneously called by a reducer.
725        ///   The code `NOT_IN_TRANSACTION` does not happen,
726        ///   as it is subsumed by `TRANSACTION_NOT_ANONYMOUS`.
727        /// - `TRANSACTION_IS_READ_ONLY`, if the pending transaction is read-only.
728        ///   This currently does not happen as anonymous read transactions
729        ///   are not exposed to modules.
730        pub fn procedure_abort_mut_tx() -> u16;
731
732        /// Perform an HTTP request as specified by the buffer `request_ptr[..request_len]`,
733        /// suspending execution until the request is complete,
734        /// then return its response details via a [`BytesSource`] written to `out[0]`
735        /// and its response body via another [`BytesSource`] written to `out[1]`.
736        ///
737        /// `request_ptr[..request_len]` should store a BSATN-serialized `spacetimedb_lib::http::Request` object
738        /// containing the details of the request to be performed.
739        ///
740        /// `body_ptr[..body_len]` should store a byte array, which will be treated as the body of the request.
741        /// `body_ptr` should be non-null and within the bounds of linear memory even when `body_len` is 0.
742        ///
743        /// If the request is successful, a [`BytesSource`] is written to `out[0]`
744        /// containing a BSATN-encoded `spacetimedb_lib::http::Response` object,
745        /// another [`BytesSource`] containing the bytes of the response body are written to `out[1]`,
746        /// and this function returns 0.
747        ///
748        /// "Successful" in this context includes any connection which results in any HTTP status code,
749        /// regardless of the specified meaning of that code.
750        /// This includes HTTP error codes such as 404 Not Found and 500 Internal Server Error.
751        ///
752        /// If the request fails, a [`BytesSource`] is written to `out[0]`
753        /// containing a BSATN-encoded [`String`] describing the failure,
754        /// and this function returns `HTTP_ERROR`.
755        /// In this case, `out[1]` is not written.
756        ///
757        /// # Errors
758        ///
759        /// Returns an error:
760        ///
761        /// - `WOULD_BLOCK_TRANSACTION` if there is currently a transaction open.
762        ///   In this case, `out` is not written.
763        /// - `BSATN_DECODE_ERROR` if `request_ptr[..request_len]` does not contain
764        ///   a valid BSATN-serialized `spacetimedb_lib::http::Request` object.
765        ///   In this case, `out` is not written.
766        /// - `HTTP_ERROR` if an error occurs while executing the HTTP request.
767        ///   In this case, a [`BytesSource`] is written to `out`
768        ///   containing a BSATN-encoded `spacetimedb_lib::http::Error` object.
769        ///
770        /// # Traps
771        ///
772        /// Traps if:
773        ///
774        /// - `request_ptr` is NULL or `request_ptr[..request_len]` is not in bounds of WASM memory.
775        /// - `body_ptr` is NULL or `body_ptr[..body_len]` is not in bounds of WASM memory.
776        /// - `out` is NULL or `out[..size_of::<RowIter>()]` is not in bounds of WASM memory.
777        /// - `request_ptr[..request_len]` does not contain a valid BSATN-serialized `spacetimedb_lib::http::Request` object.
778        #[cfg(feature = "unstable")]
779        pub fn procedure_http_request(
780            request_ptr: *const u8,
781            request_len: u32,
782            body_ptr: *const u8,
783            body_len: u32,
784            out: *mut [BytesSource; 2],
785        ) -> u16;
786    }
787
788    #[link(wasm_import_module = "spacetime_10.4")]
789    unsafe extern "C" {
790        /// Finds all rows in the index identified by `index_id`,
791        /// according to `point = point_ptr[..point_len]` in WASM memory.
792        ///
793        /// The index itself has a schema/type.
794        /// Matching defined by first BSATN-decoding `point` to that `AlgebraicType`
795        /// and then comparing the decoded `point` to the keys in the index
796        /// using `Ord for AlgebraicValue`.
797        /// to the keys in the index.
798        /// The `point` is BSATN-decoded to that `AlgebraicType`.
799        /// A match happens when `Ordering::Equal` is returned from `fn cmp`.
800        /// This occurs exactly when the row's BSATN-encoding
801        /// is equal to the encoding of the `AlgebraicValue`.
802        ///
803        /// This ABI is not limited to single column indices.
804        /// Multi-column indices can be queried by providing
805        /// a BSATN-encoded `ProductValue`
806        /// that is typed at the `ProductType` of the index.
807        ///
808        /// The relevant table for the index is found implicitly via the `index_id`,
809        /// which is unique for the module.
810        ///
811        /// On success, the iterator handle is written to the `out` pointer.
812        /// This handle can be advanced by [`row_iter_bsatn_advance`].
813        ///
814        /// # Traps
815        ///
816        /// Traps if:
817        /// - `point_ptr` is NULL or `point` is not in bounds of WASM memory.
818        /// - `out` is NULL or `out[..size_of::<RowIter>()]` is not in bounds of WASM memory.
819        ///
820        /// # Errors
821        ///
822        /// Returns an error:
823        ///
824        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
825        /// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
826        /// - `WRONG_INDEX_ALGO` if the index is not a range-scan compatible index.
827        /// - `BSATN_DECODE_ERROR`, when `point` cannot be decoded to an `AlgebraicValue`
828        ///   typed at the index's key type (`AlgebraicType`).
829        pub fn datastore_index_scan_point_bsatn(
830            index_id: IndexId,
831            point_ptr: *const u8, // AlgebraicValue
832            point_len: usize,
833            out: *mut RowIter,
834        ) -> u16;
835
836        /// Deletes all rows found in the index identified by `index_id`,
837        /// according to `point = point_ptr[..point_len]` in WASM memory.
838        ///
839        /// This syscall will delete all the rows found by
840        /// [`datastore_index_scan_point_bsatn`] with the same arguments passed.
841        /// See `datastore_index_scan_point_bsatn` for details.
842        ///
843        /// The number of rows deleted is written to the WASM pointer `out`.
844        ///
845        /// # Traps
846        ///
847        /// Traps if:
848        /// - `point_ptr` is NULL or `point` is not in bounds of WASM memory.
849        /// - `out` is NULL or `out[..size_of::<u32>()]` is not in bounds of WASM memory.
850        ///
851        /// # Errors
852        ///
853        /// Returns an error:
854        ///
855        /// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
856        /// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
857        /// - `WRONG_INDEX_ALGO` if the index is not a range-compatible index.
858        /// - `BSATN_DECODE_ERROR`, when `point` cannot be decoded to an `AlgebraicValue`
859        ///   typed at the index's key type (`AlgebraicType`).
860        pub fn datastore_delete_by_index_scan_point_bsatn(
861            index_id: IndexId,
862            point_ptr: *const u8, // AlgebraicValue
863            point_len: usize,
864            out: *mut u32,
865        ) -> u16;
866    }
867
868    /// What strategy does the database index use?
869    ///
870    /// See also: <https://www.postgresql.org/docs/current/sql-createindex.html>
871    #[repr(u8)]
872    #[non_exhaustive]
873    pub enum IndexType {
874        /// Indexing works by putting the index key into a b-tree.
875        BTree = 0,
876        /// Indexing works by hashing the index key.
877        Hash = 1,
878    }
879
880    /// The error log level. See [`console_log`].
881    pub const LOG_LEVEL_ERROR: u8 = 0;
882    /// The warn log level. See [`console_log`].
883    pub const LOG_LEVEL_WARN: u8 = 1;
884    /// The info log level. See [`console_log`].
885    pub const LOG_LEVEL_INFO: u8 = 2;
886    /// The debug log level. See [`console_log`].
887    pub const LOG_LEVEL_DEBUG: u8 = 3;
888    /// The trace log level. See [`console_log`].
889    pub const LOG_LEVEL_TRACE: u8 = 4;
890    /// The panic log level. See [`console_log`].
891    ///
892    /// A panic level is emitted just before a fatal error causes the WASM module to trap.
893    pub const LOG_LEVEL_PANIC: u8 = 101;
894
895    /// A handle into a buffer of bytes in the host environment that can be read from.
896    ///
897    /// Used for transporting bytes from host to WASM linear memory.
898    #[derive(PartialEq, Eq, Copy, Clone)]
899    #[repr(transparent)]
900    pub struct BytesSource(u32);
901
902    impl BytesSource {
903        /// An invalid handle, used e.g., when the reducer arguments were empty.
904        pub const INVALID: Self = Self(0);
905    }
906
907    /// A handle into a buffer of bytes in the host environment that can be written to.
908    ///
909    /// Used for transporting bytes from WASM linear memory to host.
910    #[derive(PartialEq, Eq, Copy, Clone)]
911    #[repr(transparent)]
912    pub struct BytesSink(u32);
913
914    /// Represents table iterators.
915    #[derive(PartialEq, Eq, Copy, Clone)]
916    #[repr(transparent)]
917    pub struct RowIter(u32);
918
919    impl RowIter {
920        /// An invalid handle, used e.g., when the iterator has been exhausted.
921        pub const INVALID: Self = Self(0);
922    }
923
924    #[cfg(any())]
925    mod module_exports {
926        type Encoded<T> = Buffer;
927        type Identity = Encoded<[u8; 32]>;
928        /// Microseconds since the unix epoch
929        type Timestamp = u64;
930        /// Buffer::INVALID => Ok(()); else errmsg => Err(errmsg)
931        type Result = Buffer;
932        extern "C" {
933            /// All functions prefixed with `__preinit__` are run first in alphabetical order.
934            /// For those it's recommended to use /etc/xxxx.d conventions of like `__preinit__20_do_thing`:
935            /// <https://man7.org/linux/man-pages/man5/sysctl.d.5.html#CONFIGURATION_DIRECTORIES_AND_PRECEDENCE>
936            fn __preinit__XX_XXXX();
937            /// Optional. Run after `__preinit__`; can return an error. Intended for dynamic languages; this
938            /// would be where you would initialize the interepreter and load the user module into it.
939            fn __setup__() -> Result;
940            /// Required. Runs after `__setup__`; returns all the exports for the module.
941            fn __describe_module__() -> Encoded<ModuleDef>;
942            /// Required. id is an index into the `ModuleDef.reducers` returned from `__describe_module__`.
943            /// args is a bsatn-encoded product value defined by the schema at `reducers[id]`.
944            fn __call_reducer__(
945                id: usize,
946                sender_0: u64,
947                sender_1: u64,
948                sender_2: u64,
949                sender_3: u64,
950                conn_id_0: u64,
951                conn_id_1: u64,
952                timestamp: u64,
953                args: Buffer,
954            ) -> Result;
955            /// Currently unused?
956            fn __migrate_database__XXXX(sender: Identity, timestamp: Timestamp, something: Buffer) -> Result;
957        }
958    }
959}
960
961/// Error values used in the safe bindings API.
962#[derive(Copy, Clone, PartialEq, Eq)]
963#[repr(transparent)]
964pub struct Errno(NonZeroU16);
965
966// once Error gets exposed from core this crate can be no_std again
967impl std::error::Error for Errno {}
968
969pub type Result<T, E = Errno> = core::result::Result<T, E>;
970
971macro_rules! def_errno {
972    ($($err_name:ident($errno:literal, $errmsg:literal),)*) => {
973        impl Errno {
974            $(#[doc = $errmsg] pub const $err_name: Errno = Errno(errno::$err_name);)*
975        }
976    };
977}
978errnos!(def_errno);
979
980impl Errno {
981    /// Returns a description of the errno value, if any.
982    pub const fn message(self) -> Option<&'static str> {
983        errno::strerror(self.0)
984    }
985
986    /// Converts the given `code` to an error number in `Errno`'s representation.
987    #[inline]
988    pub const fn from_code(code: u16) -> Option<Self> {
989        match NonZeroU16::new(code) {
990            Some(code) => Some(Errno(code)),
991            None => None,
992        }
993    }
994
995    /// Converts this `errno` into a primitive error code.
996    #[inline]
997    pub const fn code(self) -> u16 {
998        self.0.get()
999    }
1000}
1001
1002impl fmt::Debug for Errno {
1003    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1004        let mut fmt = f.debug_struct("Errno");
1005        fmt.field("code", &self.code());
1006        if let Some(msg) = self.message() {
1007            fmt.field("message", &msg);
1008        }
1009        fmt.finish()
1010    }
1011}
1012
1013impl fmt::Display for Errno {
1014    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1015        let message = self.message().unwrap_or("Unknown error");
1016        write!(f, "{message} (error {})", self.code())
1017    }
1018}
1019
1020/// Convert the status value `x` into a result.
1021/// When `x = 0`, we have a success status.
1022fn cvt(x: u16) -> Result<()> {
1023    match Errno::from_code(x) {
1024        None => Ok(()),
1025        Some(err) => Err(err),
1026    }
1027}
1028
1029/// Runs the given function `f` provided with an uninitialized `out` pointer.
1030///
1031/// Assuming the call to `f` succeeds (`Ok(_)`), the `out` pointer's value is returned.
1032///
1033/// # Safety
1034///
1035/// This function is safe to call, if and only if,
1036/// - The function `f` writes a safe and valid `T` to the `out` pointer.
1037///   It's not required to write to `out` when `f(out)` returns an error code.
1038/// - The function `f` never reads a safe and valid `T` from the `out` pointer
1039///   before writing a safe and valid `T` to it.
1040#[inline]
1041unsafe fn call<T: Copy>(f: impl FnOnce(*mut T) -> u16) -> Result<T> {
1042    unsafe {
1043        let mut out = MaybeUninit::uninit();
1044        let f_code = f(out.as_mut_ptr());
1045        cvt(f_code)?;
1046        Ok(out.assume_init())
1047    }
1048}
1049
1050/// Runs the given function `f`.
1051///
1052/// Assuming the call to `f` returns 0, `Ok(())` is returned,
1053/// and otherwise `Err(err)` is returned.
1054#[inline]
1055#[cfg(feature = "unstable")]
1056fn call_no_ret(f: impl FnOnce() -> u16) -> Result<()> {
1057    let f_code = f();
1058    cvt(f_code)?;
1059    Ok(())
1060}
1061
1062/// Queries the `table_id` associated with the given (table) `name`.
1063///
1064/// The table id is returned.
1065///
1066/// # Errors
1067///
1068/// Returns an error:
1069///
1070/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
1071/// - `NO_SUCH_TABLE`, when `name` is not the name of a table.
1072#[inline]
1073pub fn table_id_from_name(name: &str) -> Result<TableId> {
1074    unsafe { call(|out| raw::table_id_from_name(name.as_ptr(), name.len(), out)) }
1075}
1076
1077/// Queries the `index_id` associated with the given (index) `name`.
1078///
1079/// The index id is returned.
1080///
1081/// # Errors
1082///
1083/// Returns an error:
1084///
1085/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
1086/// - `NO_SUCH_INDEX`, when `name` is not the name of an index.
1087#[inline]
1088pub fn index_id_from_name(name: &str) -> Result<IndexId> {
1089    unsafe { call(|out| raw::index_id_from_name(name.as_ptr(), name.len(), out)) }
1090}
1091
1092/// Returns the number of rows currently in table identified by `table_id`.
1093///
1094/// # Errors
1095///
1096/// Returns an error:
1097///
1098/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
1099/// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.
1100#[inline]
1101pub fn datastore_table_row_count(table_id: TableId) -> Result<u64> {
1102    unsafe { call(|out| raw::datastore_table_row_count(table_id, out)) }
1103}
1104
1105/// Inserts a row into the table identified by `table_id`,
1106/// where the row is a BSATN-encoded `ProductValue`
1107/// matching the table's `ProductType` row-schema.
1108///
1109/// The `row` is `&mut` due to auto-incrementing columns.
1110/// So `row` is written to with the inserted row re-encoded.
1111///
1112/// Returns an error if
1113/// - a table with the provided `table_id` doesn't exist
1114/// - there were unique constraint violations
1115/// - `row` doesn't decode from BSATN to a `ProductValue`
1116///   according to the `ProductType` that the table's schema specifies.
1117#[inline]
1118pub fn datastore_insert_bsatn(table_id: TableId, row: &mut [u8]) -> Result<&[u8]> {
1119    let row_ptr = row.as_mut_ptr();
1120    let row_len = &mut row.len();
1121    cvt(unsafe { raw::datastore_insert_bsatn(table_id, row_ptr, row_len) }).map(|()| &row[..*row_len])
1122}
1123
1124/// Updates a row into the table identified by `table_id`,
1125/// where the row is a BSATN-encoded `ProductValue`
1126/// matching the table's `ProductType` row-schema.
1127///
1128/// The row to update is found by projecting `row`
1129/// to the type of the *unique* index identified by `index_id`.
1130/// If no row is found, `row` is inserted.
1131///
1132/// The `row` is `&mut` due to auto-incrementing columns.
1133/// So `row` is written to with the updated row re-encoded.
1134///
1135/// Returns an error if
1136/// - a table with the provided `table_id` doesn't exist
1137/// - an index with the provided `index_id` doesn't exist or if the index was not unique.
1138/// - there were unique constraint violations
1139/// - `row` doesn't decode from BSATN to a `ProductValue`
1140///   according to the `ProductType` that the table's schema specifies
1141///   or if `row` cannot project to the index's type.
1142/// - the row was not found
1143#[inline]
1144pub fn datastore_update_bsatn(table_id: TableId, index_id: IndexId, row: &mut [u8]) -> Result<&[u8]> {
1145    let row_ptr = row.as_mut_ptr();
1146    let row_len = &mut row.len();
1147    cvt(unsafe { raw::datastore_update_bsatn(table_id, index_id, row_ptr, row_len) }).map(|()| &row[..*row_len])
1148}
1149
1150/// Deletes those rows, in the table identified by `table_id`,
1151/// that match any row in the byte string `relation`.
1152///
1153/// Matching is defined by first BSATN-decoding
1154/// the byte string pointed to at by `relation` to a `Vec<ProductValue>`
1155/// according to the row schema of the table
1156/// and then using `Ord for AlgebraicValue`.
1157/// A match happens when `Ordering::Equal` is returned from `fn cmp`.
1158/// This occurs exactly when the row's BSATN-encoding is equal to the encoding of the `ProductValue`.
1159///
1160/// The number of rows deleted is returned.
1161///
1162/// # Errors
1163///
1164/// Returns an error:
1165///
1166/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
1167/// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.
1168/// - `BSATN_DECODE_ERROR`, when `rel` cannot be decoded to `Vec<ProductValue>`
1169///   where each `ProductValue` is typed at the `ProductType` the table's schema specifies.
1170#[inline]
1171pub fn datastore_delete_all_by_eq_bsatn(table_id: TableId, relation: &[u8]) -> Result<u32> {
1172    unsafe { call(|out| raw::datastore_delete_all_by_eq_bsatn(table_id, relation.as_ptr(), relation.len(), out)) }
1173}
1174
1175/// Starts iteration on each row, as BSATN-encoded, of a table identified by `table_id`.
1176/// Returns iterator handle is written to the `out` pointer.
1177/// This handle can be advanced by [`RowIter::read`].
1178///
1179/// # Errors
1180///
1181/// Returns an error:
1182///
1183/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
1184/// - `NO_SUCH_TABLE`, when `table_id` is not a known ID of a table.
1185pub fn datastore_table_scan_bsatn(table_id: TableId) -> Result<RowIter> {
1186    let raw = unsafe { call(|out| raw::datastore_table_scan_bsatn(table_id, out))? };
1187    Ok(RowIter { raw })
1188}
1189
1190/// Finds all rows in the index identified by `index_id`,
1191/// according to the `point.
1192///
1193/// The index itself has a schema/type.
1194/// Matching defined by first BSATN-decoding `point` to that `AlgebraicType`
1195/// and then comparing the decoded `point` to the keys in the index
1196/// using `Ord for AlgebraicValue`.
1197/// to the keys in the index.
1198/// The `point` is BSATN-decoded to that `AlgebraicType`.
1199/// A match happens when `Ordering::Equal` is returned from `fn cmp`.
1200/// This occurs exactly when the row's BSATN-encoding
1201/// is equal to the encoding of the `AlgebraicValue`.
1202///
1203/// This ABI is not limited to single column indices.
1204/// Multi-column indices can be queried by providing
1205/// a BSATN-encoded `ProductValue`
1206/// that is typed at the `ProductType` of the index.
1207///
1208/// The relevant table for the index is found implicitly via the `index_id`,
1209/// which is unique for the module.
1210///
1211/// On success, the iterator handle is written to the `out` pointer.
1212/// This handle can be advanced by [`RowIter::read`].
1213///
1214/// # Errors
1215///
1216/// Returns an error:
1217///
1218/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
1219/// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
1220/// - `WRONG_INDEX_ALGO` if the index is not a range-compatible index.
1221/// - `BSATN_DECODE_ERROR`, when `point` cannot be decoded to an `AlgebraicValue`
1222///   typed at the index's key type (`AlgebraicType`).
1223pub fn datastore_index_scan_point_bsatn(index_id: IndexId, point: &[u8]) -> Result<RowIter> {
1224    let raw = unsafe { call(|out| raw::datastore_index_scan_point_bsatn(index_id, point.as_ptr(), point.len(), out))? };
1225    Ok(RowIter { raw })
1226}
1227
1228/// Finds all rows in the index identified by `index_id`,
1229/// according to the `prefix`, `rstart`, and `rend`.
1230///
1231/// The index itself has a schema/type.
1232/// The `prefix` is decoded to the initial `prefix_elems` `AlgebraicType`s
1233/// whereas `rstart` and `rend` are decoded to the `prefix_elems + 1` `AlgebraicType`
1234/// where the `AlgebraicValue`s are wrapped in `Bound`.
1235/// That is, `rstart, rend` are BSATN-encoded `Bound<AlgebraicValue>`s.
1236///
1237/// Matching is then defined by equating `prefix`
1238/// to the initial `prefix_elems` columns of the index
1239/// and then imposing `rstart` as the starting bound
1240/// and `rend` as the ending bound on the `prefix_elems + 1` column of the index.
1241/// Remaining columns of the index are then unbounded.
1242/// Note that the `prefix` in this case can be empty (`prefix_elems = 0`),
1243/// in which case this becomes a ranged index scan on a single-col index
1244/// or even a full table scan if `rstart` and `rend` are both unbounded.
1245///
1246/// The relevant table for the index is found implicitly via the `index_id`,
1247/// which is unique for the module.
1248///
1249/// On success, the iterator handle is written to the `out` pointer.
1250/// This handle can be advanced by [`RowIter::read`].
1251///
1252/// # Non-obvious queries
1253///
1254/// For an index on columns `[a, b, c]`:
1255///
1256/// - `a = x, b = y` is encoded as a prefix `[x, y]`
1257///   and a range `Range::Unbounded`,
1258///   or as a  prefix `[x]` and a range `rstart = rend = Range::Inclusive(y)`.
1259/// - `a = x, b = y, c = z` is encoded as a prefix `[x, y]`
1260///   and a  range `rstart = rend = Range::Inclusive(z)`.
1261/// - A sorted full scan is encoded as an empty prefix
1262///   and a range `Range::Unbounded`.
1263///
1264/// # Errors
1265///
1266/// Returns an error:
1267///
1268/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
1269/// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
1270/// - `WRONG_INDEX_ALGO` if the index is not a range-compatible index.
1271/// - `BSATN_DECODE_ERROR`, when `prefix` cannot be decoded to
1272///   a `prefix_elems` number of `AlgebraicValue`
1273///   typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.
1274///   Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`
1275///   where the inner `AlgebraicValue`s are
1276///   typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.
1277pub fn datastore_index_scan_range_bsatn(
1278    index_id: IndexId,
1279    prefix: &[u8],
1280    prefix_elems: ColId,
1281    rstart: &[u8],
1282    rend: &[u8],
1283) -> Result<RowIter> {
1284    let raw = unsafe {
1285        call(|out| {
1286            raw::datastore_index_scan_range_bsatn(
1287                index_id,
1288                prefix.as_ptr(),
1289                prefix.len(),
1290                prefix_elems,
1291                rstart.as_ptr(),
1292                rstart.len(),
1293                rend.as_ptr(),
1294                rend.len(),
1295                out,
1296            )
1297        })?
1298    };
1299    Ok(RowIter { raw })
1300}
1301
1302/// Deletes all rows found in the index identified by `index_id`,
1303/// according to the `point.
1304///
1305/// This syscall will delete all the rows found by
1306/// [`datastore_index_scan_point_bsatn`] with the same arguments passed.
1307/// See `datastore_index_scan_point_bsatn` for details.
1308///
1309/// The number of rows deleted is returned on success.
1310///
1311/// # Errors
1312///
1313/// Returns an error:
1314///
1315/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
1316/// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
1317/// - `WRONG_INDEX_ALGO` if the index is not a range-compatible index.
1318/// - `BSATN_DECODE_ERROR`, when `point` cannot be decoded to an `AlgebraicValue`
1319///   typed at the index's key type (`AlgebraicType`).
1320pub fn datastore_delete_by_index_scan_point_bsatn(index_id: IndexId, point: &[u8]) -> Result<u32> {
1321    unsafe { call(|out| raw::datastore_delete_by_index_scan_point_bsatn(index_id, point.as_ptr(), point.len(), out)) }
1322}
1323
1324/// Deletes all rows found in the index identified by `index_id`,
1325/// according to the `prefix`, `rstart`, and `rend`.
1326///
1327/// This syscall will delete all the rows found by
1328/// [`datastore_index_scan_range_bsatn`] with the same arguments passed,
1329/// including `prefix_elems`.
1330/// See `datastore_index_scan_range_bsatn` for details.
1331///
1332/// The number of rows deleted is returned on success.
1333///
1334/// # Errors
1335///
1336/// Returns an error:
1337///
1338/// - `NOT_IN_TRANSACTION`, when called outside of a transaction.
1339/// - `NO_SUCH_INDEX`, when `index_id` is not a known ID of an index.
1340/// - `WRONG_INDEX_ALGO` if the index is not a range-compatible index.
1341/// - `BSATN_DECODE_ERROR`, when `prefix` cannot be decoded to
1342///   a `prefix_elems` number of `AlgebraicValue`
1343///   typed at the initial `prefix_elems` `AlgebraicType`s of the index's key type.
1344///   Or when `rstart` or `rend` cannot be decoded to an `Bound<AlgebraicValue>`
1345///   where the inner `AlgebraicValue`s are
1346///   typed at the `prefix_elems + 1` `AlgebraicType` of the index's key type.
1347pub fn datastore_delete_by_index_scan_range_bsatn(
1348    index_id: IndexId,
1349    prefix: &[u8],
1350    prefix_elems: ColId,
1351    rstart: &[u8],
1352    rend: &[u8],
1353) -> Result<u32> {
1354    unsafe {
1355        call(|out| {
1356            raw::datastore_delete_by_index_scan_range_bsatn(
1357                index_id,
1358                prefix.as_ptr(),
1359                prefix.len(),
1360                prefix_elems,
1361                rstart.as_ptr(),
1362                rstart.len(),
1363                rend.as_ptr(),
1364                rend.len(),
1365                out,
1366            )
1367        })
1368    }
1369}
1370
1371/// A log level that can be used in `console_log`.
1372/// The variants are convertible into a raw `u8` log level.
1373#[repr(u8)]
1374pub enum LogLevel {
1375    /// The error log level. See [`console_log`].
1376    Error = raw::LOG_LEVEL_ERROR,
1377    /// The warn log level. See [`console_log`].
1378    Warn = raw::LOG_LEVEL_WARN,
1379    /// The info log level. See [`console_log`].
1380    Info = raw::LOG_LEVEL_INFO,
1381    /// The debug log level. See [`console_log`].
1382    Debug = raw::LOG_LEVEL_DEBUG,
1383    /// The trace log level. See [`console_log`].
1384    Trace = raw::LOG_LEVEL_TRACE,
1385    /// The panic log level. See [`console_log`].
1386    ///
1387    /// A panic level is emitted just before a fatal error causes the WASM module to trap.
1388    Panic = raw::LOG_LEVEL_PANIC,
1389}
1390
1391/// Log at `level` a `text` message occurring in `filename:line_number`
1392/// with [`target`] being the module path at the `log!` invocation site.
1393///
1394/// [`target`]: https://docs.rs/log/latest/log/struct.Record.html#method.target
1395#[inline]
1396pub fn console_log(
1397    level: LogLevel,
1398    target: Option<&str>,
1399    filename: Option<&str>,
1400    line_number: Option<u32>,
1401    text: &str,
1402) {
1403    let opt_ptr = |b: Option<&str>| b.map_or(ptr::null(), |b| b.as_ptr());
1404    let opt_len = |b: Option<&str>| b.map_or(0, |b| b.len());
1405    unsafe {
1406        raw::console_log(
1407            level as u8,
1408            opt_ptr(target),
1409            opt_len(target),
1410            opt_ptr(filename),
1411            opt_len(filename),
1412            line_number.unwrap_or(u32::MAX),
1413            text.as_ptr(),
1414            text.len(),
1415        )
1416    }
1417}
1418
1419/// Schedule a reducer to be called asynchronously, nonatomically, and immediately
1420/// on a best-effort basis.
1421///
1422/// The reducer is assigned `name` and is provided `args` as its argument.
1423#[cfg(feature = "unstable")]
1424#[inline]
1425pub fn volatile_nonatomic_schedule_immediate(name: &str, args: &[u8]) {
1426    unsafe { raw::volatile_nonatomic_schedule_immediate(name.as_ptr(), name.len(), args.as_ptr(), args.len()) }
1427}
1428
1429/// Read the current module's identity, as a little-endian byte array.
1430///
1431/// Doesn't return a proper typed `Identity` because this crate doesn't depend on `spacetimedb_lib`.
1432#[inline]
1433pub fn identity() -> [u8; 32] {
1434    let mut buf = [0u8; 32];
1435    unsafe {
1436        raw::identity(buf.as_mut_ptr());
1437    }
1438    buf
1439}
1440
1441/// Finds the JWT payload associated with `connection_id`.
1442/// If nothing is found for the connection, this returns None.
1443/// If a payload is found, this will return a valid [`raw::BytesSource`].
1444///
1445/// This must be called inside a transaction (because it reads from a system table).
1446///
1447/// # Errors
1448///
1449/// This panics on any error. You can see details about errors in [`raw::get_jwt`].
1450#[inline]
1451pub fn get_jwt(connection_id: [u8; 16]) -> Option<raw::BytesSource> {
1452    let source = unsafe {
1453        call(|out| raw::get_jwt(connection_id.as_ptr(), out))
1454            .unwrap_or_else(|errno: Errno| panic!("Error getting jwt: {errno}"))
1455    };
1456
1457    if source == raw::BytesSource::INVALID {
1458        None // No JWT found.
1459    } else {
1460        Some(source)
1461    }
1462}
1463
1464pub struct RowIter {
1465    raw: raw::RowIter,
1466}
1467
1468impl RowIter {
1469    /// Read some number of BSATN-encoded rows into the provided buffer.
1470    ///
1471    /// Returns the number of new bytes added to the end of the buffer.
1472    /// When the iterator has been exhausted,
1473    /// `self.is_exhausted()` will return `true`.
1474    pub fn read(&mut self, buf: &mut Vec<u8>) -> usize {
1475        loop {
1476            let buf_ptr = buf.spare_capacity_mut();
1477            let mut buf_len = buf_ptr.len();
1478            let ret = unsafe { raw::row_iter_bsatn_advance(self.raw, buf_ptr.as_mut_ptr().cast(), &mut buf_len) };
1479            if let -1 | 0 = ret {
1480                // SAFETY: `_row_iter_bsatn_advance` just wrote `buf_len` bytes into the end of `buf`.
1481                unsafe { buf.set_len(buf.len() + buf_len) };
1482            }
1483
1484            const TOO_SMALL: i16 = errno::BUFFER_TOO_SMALL.get() as i16;
1485            match ret {
1486                -1 => {
1487                    self.raw = raw::RowIter::INVALID;
1488                    return buf_len;
1489                }
1490                0 => return buf_len,
1491                TOO_SMALL => buf.reserve(buf_len),
1492                e => panic!("unexpected error from `_row_iter_bsatn_advance`: {e}"),
1493            }
1494        }
1495    }
1496
1497    /// Returns whether the iterator is exhausted or not.
1498    pub fn is_exhausted(&self) -> bool {
1499        self.raw == raw::RowIter::INVALID
1500    }
1501}
1502
1503impl Drop for RowIter {
1504    fn drop(&mut self) {
1505        // Avoid this syscall when `_row_iter_bsatn_advance` above
1506        // notifies us that the iterator is exhausted.
1507        if self.is_exhausted() {
1508            return;
1509        }
1510        unsafe {
1511            raw::row_iter_bsatn_close(self.raw);
1512        }
1513    }
1514}
1515
1516#[cfg(feature = "unstable")]
1517pub mod procedure {
1518    //! Side-effecting or asynchronous operations which only procedures are allowed to perform.
1519
1520    use super::{call, call_no_ret, raw, Result};
1521
1522    #[inline]
1523    pub fn sleep_until(wake_at_timestamp: i64) -> i64 {
1524        // Safety: Just calling an `extern "C"` function.
1525        // Nothing weird happening here.
1526        unsafe { raw::procedure_sleep_until(wake_at_timestamp) }
1527    }
1528
1529    /// Starts a mutable transaction,
1530    /// blocking until a mutable transaction lock is acquired.
1531    ///
1532    /// Once complete, returns `Ok(timestamp)` on success,
1533    /// enabling further calls that require a pending transaction,
1534    /// or [`Errno`] otherwise.
1535    ///
1536    /// # Errors
1537    ///
1538    /// Returns an error:
1539    ///
1540    /// - `WOULD_BLOCK_TRANSACTION`, if there's already an ongoing transaction.
1541    #[inline]
1542    pub fn procedure_start_mut_tx() -> Result<i64> {
1543        unsafe { call(|out| raw::procedure_start_mut_tx(out)) }
1544    }
1545
1546    /// Commits a mutable transaction,
1547    /// blocking until the transaction has been committed
1548    /// and subscription queries have been run and broadcast.
1549    ///
1550    /// Once complete, returns `Ok(())` on success, or an [`Errno`] otherwise.
1551    ///
1552    /// # Errors
1553    ///
1554    /// Returns an error:
1555    ///
1556    /// - `TRANSACTION_NOT_ANONYMOUS`,
1557    ///   if the transaction was not started in [`procedure_start_mut_tx`].
1558    ///   This can happen if this syscall is erroneously called by a reducer.
1559    ///   The code `NOT_IN_TRANSACTION` does not happen,
1560    ///   as it is subsumed by `TRANSACTION_NOT_ANONYMOUS`.
1561    /// - `TRANSACTION_IS_READ_ONLY`, if the pending transaction is read-only.
1562    ///   This currently does not happen as anonymous read transactions
1563    ///   are not exposed to modules.
1564    #[inline]
1565    pub fn procedure_commit_mut_tx() -> Result<()> {
1566        call_no_ret(|| unsafe { raw::procedure_commit_mut_tx() })
1567    }
1568
1569    /// Aborts a mutable transaction,
1570    /// blocking until the transaction has been rolled back.
1571    ///
1572    /// Once complete, returns `Ok(())` on success, or an [`Errno`] otherwise.
1573    ///
1574    /// # Errors
1575    ///
1576    /// Returns an error:
1577    ///
1578    /// - `TRANSACTION_NOT_ANONYMOUS`,
1579    ///   if the transaction was not started in [`procedure_start_mut_tx`].
1580    ///   This can happen if this syscall is erroneously called by a reducer.
1581    ///   The code `NOT_IN_TRANSACTION` does not happen,
1582    ///   as it is subsumed by `TRANSACTION_NOT_ANONYMOUS`.
1583    /// - `TRANSACTION_IS_READ_ONLY`, if the pending transaction is read-only.
1584    ///   This currently does not happen as anonymous read transactions
1585    ///   are not exposed to modules.
1586    #[inline]
1587    pub fn procedure_abort_mut_tx() -> Result<()> {
1588        call_no_ret(|| unsafe { raw::procedure_abort_mut_tx() })
1589    }
1590
1591    #[inline]
1592    #[cfg(feature = "unstable")]
1593    /// Perform an HTTP request as specified by `http_request_bsatn`,
1594    /// suspending execution until the request is complete,
1595    /// then return its response or error.
1596    ///
1597    /// `http_request_bsatn` should be a BSATN-serialized `spacetimedb_lib::http::Request`.
1598    ///
1599    /// If the request completes successfully,
1600    /// this function returns `Ok(bytes)`, where `bytes` contains a BSATN-serialized `spacetimedb_lib::http::Response`.
1601    /// All HTTP response codes are treated as successful for these purposes;
1602    /// this method only returns an error if it is unable to produce any HTTP response whatsoever.
1603    /// In that case, this function returns `Err(bytes)`, where `bytes` contains a BSATN-serialized `spacetimedb_lib::http::Error`.
1604    pub fn http_request(
1605        http_request_bsatn: &[u8],
1606        body: &[u8],
1607    ) -> Result<(raw::BytesSource, raw::BytesSource), raw::BytesSource> {
1608        let mut out = [raw::BytesSource::INVALID; 2];
1609
1610        let res = unsafe {
1611            super::raw::procedure_http_request(
1612                http_request_bsatn.as_ptr(),
1613                http_request_bsatn.len() as u32,
1614                body.as_ptr(),
1615                body.len() as u32,
1616                &mut out as *mut [raw::BytesSource; 2],
1617            )
1618        };
1619
1620        match super::Errno::from_code(res) {
1621            // Success: `out` is a `spacetimedb_lib::http::Response`.
1622            None => Ok((out[0], out[1])),
1623            // HTTP_ERROR: `out` is a `spacetimedb_lib::http::Error`.
1624            Some(errno) if errno == super::Errno::HTTP_ERROR => Err(out[0]),
1625            Some(errno) => panic!("{errno}"),
1626        }
1627    }
1628}