SDK generation
The SDK plugin generates a typed package from the database's stored definitions. While the database is open, it updates that package in the configured output directory.
The generated code calls the language binding and provides typed access to stored definitions.
Enable sdk in the plugin selection at open, with the output settings below.
Calls without it report the unavailable status.
Query results recursively omit fields containing empty objects or arrays.
Generated rows use Value::Null for absent dynamic fields and empty collections for absent list or map fields.
Null, empty strings, false, zero and array positions stay intact in the returned document.
Native time values
Generated native time fields use
std::time::SystemTime for timestamps and stardust::Dur for signed stored durations.
Timeout arguments use std::time::Duration, which cannot represent negative values.
The binding supplies the generated Serde adapters and checked instant_from_ticks, instant_to_ticks, duration_from_ticks and duration_to_ticks conversions.
The explicit tick conversions refuse precision loss and values outside the target range. These conversions perform no native operation.
A native time schema requires one string member, #utc or #dur, and sets additionalProperties to false.
For example, this schema describes a native timestamp:
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 4C 74797065 | txt "type" |
| 09 | 6C 737472696E67 | txt "string" |
| 10 | 0F 0A 04000000 0900 0000 | map leaf, size 10: @4 → @9 |
| 1A | 4C 23757463 | txt "#utc" |
| 1F | 0F 0A 1A000000 1000 0000 | map leaf, size 10: @1A → @10 |
| 29 | 4C 23757463 | txt "#utc" |
| 2E | 0E 0D 00 0100 010000 00 29000000 | array leaf, size 13, shift 0, bitmap 0001, count 1 (root): @29 |
| 3B | AC 70726F70657274 696573 | txt "properties" |
| 46 | 0F 0A 3B000000 1F00 0000 | map leaf, size 10: @3B → @1F |
| 50 | 8C 72657175697265 64 | txt "required" |
| 59 | 0F 0A 50000000 2E00 0000 | map leaf, size 10: @50 → @2E |
| 63 | 14 14 616464697469 6F6E616C50726F70 657274696573 | txt "additionalProperties" |
| 79 | 01 | bit false |
| 7A | 0F 0A 63000000 7900 0000 | map leaf, size 10: @63 → @79 |
| 84 | 4C 74797065 | txt "type" |
| 89 | 6C 6F626A656374 | txt "object" |
| 90 | 0F 0A 84000000 8900 0000 | map leaf, size 10: @84 → @89 |
| 9A | 07 16 A8040000 4600 0000 59000000 7A00 0000 90000000 | map branch, size 22, depth 0 (root), slots 3, 5, 7, 10: @46 → @59 → @7A → @90 |
| B0 | 9A000000 00000000 | root → @9A; previous: none |
Use #dur in both places for a duration schema.
Schema fields support lists and stored-schema references.
Scalar time fields also type query filters and rows.
Native time expression results use the same binding types.
An ordinary string schema stays text, including strings with a date-time format.
Native ticks are signed 64-bit counts of 100 ns.
Timestamp ticks count from the Unix epoch in UTC. Duration ticks can be negative.
Runs
The plugin generates once at open, and again after commits that change definitions. Data commits do not start a run. A run reads all definitions from one committed snapshot.
Each successful render replaces the output directory's contents, including the startup run. See file ownership before choosing an output directory.
Call status to inspect these fields:
| Field | Contents |
|---|---|
enabled | Whether the plugin is enabled |
pending | Whether generation remains |
runs | Run count |
fingerprint | Generation model fingerprint |
outputs | Outputs, each with language, directory and file counts written and removed |
diagnostics | Diagnostics with severity, code, definition type and path, pointer, member, language and message |
failure | Last run failure, with code and message; absent after a successful run |
When to call sync
Generation runs automatically at open and after definition commits, according to the debounce settings. Opening the database or committing definitions can finish before the generated files are ready. Call sync before compiling or reading files that must include those definitions. Applications that can use the files after automatic generation finishes do not need an explicit sync.
The call requests a render immediately, bypassing debounce. It also rebuilds files when definitions have not changed.
Its options accept only the common maxWait option. A timeout leaves the requested run active.
Repeated waits after a timeout join that run. Inspect status when generation reports failed.
Without an output language, sync validates the model inline and writes no files.
Settings
Set rust.dir to a dedicated output directory. Enable the serde feature of the stardust crate.
The generated structs use serde derives directly. Add serde with its derive feature to your application's Cargo.toml:
[dependencies]
serde = { version = "1", features = ["derive"] }Keep your Cargo project and application source outside the output directory.
The plugin writes mod.rs and one module for each definition type that has generated APIs.
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 3C 646972 | txt "dir" |
| 08 | AC 67656E2F70656F 706C65 | txt "gen/people" |
| 13 | 0F 0A 04000000 0800 0000 | map leaf, size 10: @4 → @8 |
| 1D | 4C 23647572 | txt "#dur" |
| 22 | 7C 5054302E323553 | txt "PT0.25S" |
| 2A | 0F 0A 1D000000 2200 0000 | map leaf, size 10: @1D → @22 |
| 34 | 4C 23647572 | txt "#dur" |
| 39 | 4C 50543253 | txt "PT2S" |
| 3E | 0F 0A 34000000 3900 0000 | map leaf, size 10: @34 → @39 |
| 48 | 8C 6D61785F776169 74 | txt "max_wait" |
| 51 | 0F 0A 48000000 3E00 0000 | map leaf, size 10: @48 → @3E |
| 5B | 8C 6465626F756E63 65 | txt "debounce" |
| 64 | 0F 0A 5B000000 2A00 0000 | map leaf, size 10: @5B → @2A |
| 6E | 4C 72757374 | txt "rust" |
| 73 | 0F 0A 6E000000 1300 0000 | map leaf, size 10: @6E → @13 |
| 7D | 07 12 0D000000 5100 0000 64000000 7300 0000 | map branch, size 18, depth 0 (root), slots 0, 2, 3: @51 → @64 → @73 |
| 8F | 3C 73646B | txt "sdk" |
| 93 | 0F 0A 8F000000 7D00 0000 | map leaf, size 10: @8F → @7D |
| 9D | 93000000 00000000 | root → @93; previous: none |
Relative paths start at the process working directory when you open the database.
Call database.sdk().sync(Some(&sync_options))? before compilation, with native options such as {maxWait {#dur PT5S}}.
The Rust modules
Import the generated mod.rs into your application with a Rust #[path] module declaration.
Each module exposes MODEL_FINGERPRINT, the deterministic fingerprint of the shared generation model.
schemascontains serde structs with original wire names. Optional fields useOption<T>. The serializer writes a field only when its value isSome(value).queriescontains parameter structs, inferred row structs and functions that returnstardust::OwnedValues<Row>.mutationscontains parameter structs and functions that commit stored mutations and returnstardust::Commit.expressionscontains parameter structs and functions that evaluate stored expressions with the database's current macros.
The functions run synchronously on the calling thread. Parameter structs pass by value, and decoded rows own their data. Query cursors implement Iterator.
The database must remain open while you advance the cursor. The cursor owns its query and rows. It releases them on exhaustion or on the first error. Drop or cursor.close() releases the cursor immediately.
Each row owns its data. Expression functions decode and release the owned result document before returning.
Binding conversion errors include the field path.
Declared defaulted parameters and optional filters use Option<T> with serde omission.
None keeps a stored default or leaves a filter unbound. Some(value) supplies the value, including zero, false or an empty collection.
Variables used only in predicates remain required. Entity filters use stardust::Link.
Aggregate outputs, window outputs and compiler result slots are not filter inputs.
Some(stardust::Value::Null) supplies explicit null where a dynamic input permits it. A declared parameter with null reports missing_parameter.
For these stored definitions:
| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 1C 2A | txt "*" |
| 06 | 2C 3F6E | txt "?n" |
| 09 | 02 02000000000000 00 | i64 2 |
| 12 | 0E 15 00 0700 030000 00 04000000 060000 00 09000000 | array leaf, size 21, shift 0, bitmap 0007, count 3 (root): @4 → @6 → @9 |
| 27 | 5C 3F6974656D | txt "?item" |
| 2D | 0E 0D 00 0100 010000 00 27000000 | array leaf, size 13, shift 0, bitmap 0001, count 1 (root): @27 |
| 3A | 5C 3F6974656D | txt "?item" |
| 40 | 3C 6B6579 | txt "key" |
| 44 | 4C 3F6B6579 | txt "?key" |
| 49 | 0E 15 00 0700 030000 00 3A000000 400000 00 44000000 | array leaf, size 21, shift 0, bitmap 0007, count 3 (root): @3A → @40 → @44 |
| 5E | 0E 0D 00 0100 010000 00 49000000 | array leaf, size 13, shift 0, bitmap 0001, count 1 (root): @49 |
| 6B | 5C 7768657265 | txt "where" |
| 71 | 0F 0A 6B000000 5E00 0000 | map leaf, size 10: @6B → @5E |
| 7B | 4C 66696E64 | txt "find" |
| 80 | 0F 0A 7B000000 2D00 0000 | map leaf, size 10: @7B → @2D |
| 8A | 07 0E 0A000000 7100 0000 80000000 | map branch, size 14, depth 0 (root), slots 1, 3: @71 → @80 |
| 98 | DC 71756572696573 2F6974656D73 | txt "queries/items" |
| A6 | 0F 0A 98000000 8A00 0000 | map leaf, size 10: @98 → @8A |
| B0 | 14 12 657870726573 73696F6E732F646F 75626C65 | txt "expressions/double" |
| C4 | 0F 0A B0000000 1200 0000 | map leaf, size 10: @B0 → @12 |
| CE | 07 0E 08010000 A600 0000 C4000000 | map branch, size 14, depth 0 (root), slots 3, 8: @A6 → @C4 |
| DC | CE000000 00000000 | root → @CE; previous: none |
let doubled = expressions::double(db, expressions::DoubleParameters { n: 21 })?;
assert_eq!(doubled, 42);
let parameters = queries::ItemsParameters {
item: None,
key: Some(stardust::Value::String("b".into())),
};
let mut rows = queries::items(db, parameters, &stardust::RunOptions::default())?;
while let Some(row) = rows.next() {
let item = row?;
assert_ne!(item.item, stardust::Link(0));
}| Definition type | Rust type |
|---|---|
string | String |
integer | i64 |
number | f64 |
boolean | bool |
object without a schema | BTreeMap<String, stardust::Value> |
array | Vec<T> |
| Native timestamp | std::time::SystemTime |
| Native signed duration | stardust::Dur |
| Entity | stardust::Link |
| Not known | stardust::Value |
Direct references to stored object schemas use Box<T> so recursive records have finite size.
Arrays of schema records use Vec<T>. Native time references use the time types described above.
Other non-object references use the dynamic value fallback.
The generated code qualifies standard and binding types, so authored schema names cannot change their meaning.
Names
Struct names use PascalCase. Functions and fields use snake_case. Serde renames keep the original wire names, including leading underscores.
Each definition type has its own module. A schema and a query can share a definition name.
Member collisions, derived-name collisions and reserved-name collisions fail with Model_Errors before publication.
The plugin preserves previous output after a model error. Rename the conflicting definition or member and synchronize again.
File ownership
The plugin owns the entire configured output directory. Every successful render removes its previous contents, including edited files, unrelated files and subdirectories.
Keep application source, database files and other data outside this directory.
Do not edit generated files: the next run replaces your changes.
The plugin retains only its internal .stardust-sdk.lock file between runs.
The plugin renders and validates the package before it removes any files. Rendering errors leave the previous directory contents unchanged. A filesystem error during cleanup or writing can leave the directory empty or partly written. The next successful run clears that partial output and writes a fresh package. Use sync before a build and check its result.
Each file is written through an atomic rename. Rebuilding the whole directory is not atomic.
A destination lock prevents concurrent publication and reports Destination_Busy when another publisher holds it.
Use a separate output directory for each database, with no directory nested inside another output directory.
Cleanup removes symlinks inside the output directory without following them.
The output directory itself and the lock file must not be symlinks.
Directory inspection is limited to 65,536 entries, 64 levels and 16 MiB of stored paths.
An inspection failure leaves the previous contents unchanged.
Operations
| Operation | Returns | Summary |
|---|---|---|
| Sdk::status | document | sdk.status() returns the SDK generator's owned status document. |
| Sdk::sync | none | sdk.sync(options) waits until the SDK generator published the definitions committed before the call. |
Sdk::status
pub fn status(&self) -> Result<Document>Parameters
| Parameter | Type | Ownership | Default | Meaning |
|---|---|---|---|---|
self | &Self | Borrowed for the call | Required | The sdk owner for this operation. Keep it alive during the call. |
Errors
| Status | When |
|---|---|
Status::UNAVAILABLE | the database does not enable the sdk plugin |
Status::CLOSED | the database is closed |
Status::OUT_OF_MEMORY | native memory is exhausted |
Notes
The document describes generation outputs, diagnostics and the last run: see runs.
Examples
Describe the SDK generator
let sync_options = stardust::parse("{maxWait {#dur PT5S}}", Format::DUST)?;
db.sdk().sync(Some(&sync_options))?;
let result = db.sdk().status()?;| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 4C 72756E73 | txt "runs" |
| 09 | 02 01000000000000 00 | i64 1 |
| 12 | 0F 0A 04000000 0900 0000 | map leaf, size 10: @4 → @9 |
| 1C | 7C 656E61626C6564 | txt "enabled" |
| 24 | 09 | bit true |
| 25 | 0F 0A 1C000000 2400 0000 | map leaf, size 10: @1C → @24 |
| 2F | 7C 70656E64696E67 | txt "pending" |
| 37 | 01 | bit false |
| 38 | 0F 0A 2F000000 3700 0000 | map leaf, size 10: @2F → @37 |
| 42 | BC 66696E67657270 72696E74 | txt "fingerprint" |
| 4E | 14 47 736861323536 3A66306531373061 6335346236376230 6639613865343739 3439323261353836 6332316461323931 3062303736366463 6433666562343130 6234313662363165 38 | txt "sha256:f0e170ac54b67b0f9…" |
| 97 | 0F 0A 42000000 4E00 0000 | map leaf, size 10: @42 → @4E |
| A1 | 07 16 280A0000 1200 0000 25000000 3800 0000 97000000 | map branch, size 22, depth 0 (root), slots 3, 5, 9, 11: @12 → @25 → @38 → @97 |
| B7 | A1000000 00000000 | root → @A1; previous: none |
C exports: stardust_db_plugin_call, stardust_result_take_document, stardust_result_error, stardust_result_release
Sdk::sync
pub fn sync(&self, options: Option<&Document>) -> Result<()>Parameters
| Parameter | Type | Ownership | Default | Meaning |
|---|---|---|---|---|
self | &Self | Borrowed for the call | Required | The sdk owner for this operation. Keep it alive during the call. |
options | Option<&Document> | Borrowed for the call | Absent selects operation defaults | The borrowed call options, or none. Optional maxWait is a nonnegative native duration, such as {maxWait {#dur PT5S}}. Without it, or with zero, the call checks once. |
Errors
| Status | When |
|---|---|
Status::INVALID_ARGUMENT | options is not an object, or an option is invalid, duplicated or unsupported |
Status::FAILED | the plugin's work failed; status describes the failure |
Status::TIMEOUT | maxWait passed before the work finished |
Status::UNAVAILABLE | the database does not enable the sdk plugin |
Status::CLOSED | the database is closed |
Notes
Database close interrupts an active wait. See when to call sync.
Examples
Run the SDK generator and wait for it
let sync_options = stardust::parse("{maxWait {#dur PT5S}}", Format::DUST)?;
db.sdk().sync(Some(&sync_options))?;
let result = db.sdk().status()?;| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 4C 72756E73 | txt "runs" |
| 09 | 02 01000000000000 00 | i64 1 |
| 12 | 0F 0A 04000000 0900 0000 | map leaf, size 10: @4 → @9 |
| 1C | 7C 656E61626C6564 | txt "enabled" |
| 24 | 09 | bit true |
| 25 | 0F 0A 1C000000 2400 0000 | map leaf, size 10: @1C → @24 |
| 2F | 7C 70656E64696E67 | txt "pending" |
| 37 | 01 | bit false |
| 38 | 0F 0A 2F000000 3700 0000 | map leaf, size 10: @2F → @37 |
| 42 | BC 66696E67657270 72696E74 | txt "fingerprint" |
| 4E | 14 47 736861323536 3A66306531373061 6335346236376230 6639613865343739 3439323261353836 6332316461323931 3062303736366463 6433666562343130 6234313662363165 38 | txt "sha256:f0e170ac54b67b0f9…" |
| 97 | 0F 0A 42000000 4E00 0000 | map leaf, size 10: @42 → @4E |
| A1 | 07 16 280A0000 1200 0000 25000000 3800 0000 97000000 | map branch, size 22, depth 0 (root), slots 3, 5, 9, 11: @12 → @25 → @38 → @97 |
| B7 | A1000000 00000000 | root → @A1; previous: none |
C exports: stardust_db_plugin_call, stardust_result_take_document, stardust_result_error, stardust_result_release