thing-contract: the gamecult.thing.* kinds as CultCache entries and unions (first commit, build not yet verified)

Does not build-verified at this commit; tests follow.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-12 03:51:58 +02:00
commit 852c34b60a
12 changed files with 1933 additions and 0 deletions

1
.gitignore vendored
View file

@ -25,3 +25,4 @@ flutter/eve_parity/assets/repixelizer/
*.deb
*.ipa
*.dSYM/
/target

602
Cargo.lock generated Normal file
View file

@ -0,0 +1,602 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "android_system_properties"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc"
dependencies = [
"libc",
]
[[package]]
name = "anyhow"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
[[package]]
name = "autocfg"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "bumpalo"
version = "3.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "cc"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6651c9ed80effdc7db0ff72512157f901af5e3549e341e24b1dd4887d836d838"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
[[package]]
name = "chrono"
version = "0.4.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
dependencies = [
"iana-time-zone",
"js-sys",
"num-traits",
"serde",
"wasm-bindgen",
"windows-link",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "cultcache-rs"
version = "0.2.0"
source = "git+https://github.com/GameCult/CultLib.git?rev=b3bd8dd5e5b208b2012392e9c5d6dfee2441aaa7#b3bd8dd5e5b208b2012392e9c5d6dfee2441aaa7"
dependencies = [
"anyhow",
"chrono",
"cultcache-rs-derive",
"fs2",
"redb",
"rmp-serde",
"serde",
"serde_bytes",
"serde_json",
"uuid",
"windows-sys",
]
[[package]]
name = "cultcache-rs-derive"
version = "0.1.0"
source = "git+https://github.com/GameCult/CultLib.git?rev=b3bd8dd5e5b208b2012392e9c5d6dfee2441aaa7#b3bd8dd5e5b208b2012392e9c5d6dfee2441aaa7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484"
[[package]]
name = "fs2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "futures-core"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
[[package]]
name = "futures-task"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd"
[[package]]
name = "futures-util"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"slab",
]
[[package]]
name = "getrandom"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
"libc",
"r-efi",
]
[[package]]
name = "iana-time-zone"
version = "0.1.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"log",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "js-sys"
version = "0.3.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5"
dependencies = [
"cfg-if",
"futures-util",
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78"
[[package]]
name = "log"
version = "0.4.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
[[package]]
name = "memchr"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "redb"
version = "4.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb338a6c67830a61bed824b78c2bb034ab1ff401a20616bd7957524f4fdc2f22"
dependencies = [
"libc",
]
[[package]]
name = "rmp"
version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c"
dependencies = [
"num-traits",
]
[[package]]
name = "rmp-serde"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155"
dependencies = [
"rmp",
"serde",
]
[[package]]
name = "rustversion"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
[[package]]
name = "serde"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_bytes"
version = "0.11.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8"
dependencies = [
"serde",
"serde_core",
]
[[package]]
name = "serde_core"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.8",
]
[[package]]
name = "serde_json"
version = "1.0.154"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "shlex"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "syn"
version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "3.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01016da373cd8f7ef12624f796309f5c31ba8d646dd08856c02cd741d823c622"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thing-contract"
version = "0.1.0"
dependencies = [
"anyhow",
"cultcache-rs",
"rmp-serde",
"serde",
]
[[package]]
name = "unicode-ident"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
[[package]]
name = "uuid"
version = "1.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cc1186384beb7dd8eedea376413fd654937285ea6c9cfbb928dc3043ea4b606"
dependencies = [
"getrandom",
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.129"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.129"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.129"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn 3.0.8",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.129"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6"
dependencies = [
"unicode-ident",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.62.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings",
]
[[package]]
name = "windows-implement"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "windows-interface"
version = "0.59.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-result"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "zmij"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"

9
Cargo.toml Normal file
View file

@ -0,0 +1,9 @@
[workspace]
members = ["crates/*"]
resolver = "2"
[workspace.dependencies]
anyhow = "1"
cultcache-rs = { git = "https://github.com/GameCult/CultLib.git", rev = "b3bd8dd5e5b208b2012392e9c5d6dfee2441aaa7", package = "cultcache-rs" }
serde = { version = "1", features = ["derive"] }
rmp-serde = "1.3"

View file

@ -0,0 +1,14 @@
[package]
name = "thing-contract"
version = "0.1.0"
edition = "2024"
license = "MIT"
publish = false
[dependencies]
anyhow = { workspace = true }
cultcache-rs = { workspace = true }
serde = { workspace = true }
[dev-dependencies]
rmp-serde = { workspace = true }

View file

@ -0,0 +1,38 @@
//! An asset catalog: the images a provider's surfaces reference by catalog id and asset id.
use cultcache_rs::{CultSlots, DatabaseEntry};
use crate::keys::{ProviderId, asset_catalog_key};
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct AssetEntry {
#[cultcache(key = 0)]
pub id: String,
#[cultcache(key = 1)]
pub media_type: String,
#[cultcache(key = 2)]
pub uri: String,
#[cultcache(key = 3)]
pub width: i32,
#[cultcache(key = 4)]
pub height: i32,
}
#[derive(Clone, Debug, PartialEq, DatabaseEntry)]
#[cultcache(type = "gamecult.thing.asset_catalog", schema = "gamecult.thing.asset_catalog.v1")]
pub struct AssetCatalog {
#[cultcache(key = 0)]
pub provider_id: ProviderId,
#[cultcache(key = 1)]
pub catalog_id: String,
#[cultcache(key = 2)]
pub version: i64,
#[cultcache(key = 3)]
pub assets: Vec<AssetEntry>,
}
impl AssetCatalog {
pub fn record_key(&self) -> String {
asset_catalog_key(&self.provider_id, &self.catalog_id)
}
}

View file

@ -0,0 +1,120 @@
//! Commands: what a surface declares, and the one envelope a consumer sends. The caller's
//! identity has no field here: the transport sets it.
use cultcache_rs::{CultSlots, CultUnion, DatabaseEntry};
use crate::keys::{CommandId, NodeId, SchemaId, invocation_key};
use crate::source::ParamName;
/// A command a surface declares. A node may reference only a declared command.
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct CommandDecl {
#[cultcache(key = 0)]
pub id: CommandId,
#[cultcache(key = 1)]
pub payload_schema: SchemaId,
/// The input nodes whose current values travel with an invocation.
#[cultcache(key = 2)]
pub captures: Vec<NodeId>,
#[cultcache(key = 3)]
pub confirm: Option<String>,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct CommandIdRef {
#[cultcache(key = 0)]
pub id: CommandId,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum CommandRef {
#[cultcache(key = 0)]
Id(CommandIdRef),
#[cultcache(key = 1)]
Param(ParamName),
}
/// A typed record of the declared `payload_schema`, as its MessagePack bytes.
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct RecordPayload {
#[cultcache(key = 0)]
pub schema: SchemaId,
#[cultcache(key = 1, bytes)]
pub bytes: Vec<u8>,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct TextValue {
#[cultcache(key = 0)]
pub value: String,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct NumberValue {
#[cultcache(key = 0)]
pub value: f64,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct BoolValue {
#[cultcache(key = 0)]
pub value: bool,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct ChoiceValue {
#[cultcache(key = 0)]
pub value: String,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum InputValue {
#[cultcache(key = 0)]
Text(TextValue),
#[cultcache(key = 1)]
Number(NumberValue),
#[cultcache(key = 2)]
Bool(BoolValue),
#[cultcache(key = 3)]
Choice(ChoiceValue),
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct CapturedInput {
#[cultcache(key = 0)]
pub node: NodeId,
#[cultcache(key = 1)]
pub value: InputValue,
}
/// A consumer's request that a provider run a declared command.
#[derive(Clone, Debug, PartialEq, DatabaseEntry)]
#[cultcache(type = "gamecult.thing.command_invocation", schema = "gamecult.thing.command_invocation.v1")]
pub struct CommandInvocation {
/// A UUID string, unique per invocation.
#[cultcache(key = 0)]
pub invocation_id: String,
#[cultcache(key = 1)]
pub surface_key: String,
#[cultcache(key = 2)]
pub surface_version: i64,
#[cultcache(key = 3)]
pub command: CommandId,
#[cultcache(key = 4)]
pub payload: RecordPayload,
#[cultcache(key = 5)]
pub captured: Vec<CapturedInput>,
#[cultcache(key = 6)]
pub client: String,
/// RFC 3339, UTC.
#[cultcache(key = 7)]
pub issued_at: String,
#[cultcache(key = 8)]
pub delegation: Option<String>,
}
impl CommandInvocation {
pub fn record_key(&self) -> String {
invocation_key(&self.invocation_id)
}
}

View file

@ -0,0 +1,63 @@
//! Identifier newtypes and the record key of each kind. No caller spells a key as a free string.
use serde::{Deserialize, Serialize};
macro_rules! id_type {
($(#[$doc:meta])* $name:ident) => {
$(#[$doc])*
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct $name(String);
impl $name {
pub fn new(value: impl Into<String>) -> Self {
Self(value.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
};
}
id_type!(
/// A provider of surfaces and state.
ProviderId
);
id_type!(
/// The schema id of a CultCache entry type, for example `gamecult.thing.surface.v1`.
SchemaId
);
id_type!(
/// The record key of a state record.
RecordKey
);
id_type!(
/// A node id, unique within its document after composition expansion.
NodeId
);
id_type!(
/// A command declared by a surface.
CommandId
);
pub(crate) fn surface_key(provider: &ProviderId, label: &str) -> String {
format!("thing:surface:{}.{}", provider.as_str(), label)
}
pub(crate) fn composition_key(publisher: &str, name: &str, version: i32) -> String {
format!("thing:composition:{publisher}.{name}.v{version}")
}
pub(crate) fn invocation_key(invocation_id: &str) -> String {
format!("thing:invocation:{invocation_id}")
}
pub(crate) fn result_key(invocation_id: &str, seq: i32) -> String {
format!("thing:result:{invocation_id}.{seq}")
}
pub(crate) fn asset_catalog_key(provider: &ProviderId, catalog_id: &str) -> String {
format!("thing:assets:{}.{}", provider.as_str(), catalog_id)
}

View file

@ -0,0 +1,37 @@
//! The `gamecult.thing.*` wire types and their bytes. This crate owns the contract: the
//! golden payloads it blesses are what every other language package and core are held to.
//!
//! Every kind is a CultCache `DatabaseEntry`; every closed set is a `CultUnion` written
//! `[armKey, [slots...]]`, the shape the C# reference reads and writes.
mod asset;
mod command;
mod keys;
mod node;
mod result;
mod source;
mod style;
pub use asset::*;
pub use command::*;
pub use keys::{CommandId, NodeId, ProviderId, RecordKey, SchemaId};
pub use node::*;
pub use result::*;
pub use source::*;
pub use style::*;
use cultcache_rs::{CultCache, CultCacheRegistry};
/// Registers the five kinds with a cache.
pub struct ThingContract;
impl CultCacheRegistry for ThingContract {
fn register_entries(&self, cache: &mut CultCache) -> anyhow::Result<()> {
cache.register_entry_type::<SurfaceDocument>()?;
cache.register_entry_type::<CompositionDocument>()?;
cache.register_entry_type::<CommandInvocation>()?;
cache.register_entry_type::<CommandResult>()?;
cache.register_entry_type::<AssetCatalog>()?;
Ok(())
}
}

View file

@ -0,0 +1,454 @@
//! Thing Script: the closed node union, the surface document that holds a tree of nodes, and the
//! composition document that names a reusable subtree. Arm keys are fixed for v1.
use cultcache_rs::{CultSlots, CultUnion, DatabaseEntry};
use crate::command::{CommandDecl, CommandRef};
use crate::keys::{NodeId, ProviderId, composition_key, surface_key};
use crate::source::{Bind, BoolSource, CollectionSource, MemberPath, NumberSource, TextSource};
use crate::style::{BoxStyle, Paint, Role};
/// What every node carries.
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct NodeCommon {
#[cultcache(key = 0)]
pub id: NodeId,
#[cultcache(key = 1)]
pub visible: BoolSource,
#[cultcache(key = 2)]
pub a11y_label: Option<TextSource>,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum Node {
#[cultcache(key = 0)]
Box(BoxNode),
#[cultcache(key = 1)]
Text(TextNode),
#[cultcache(key = 2)]
Image(ImageNode),
#[cultcache(key = 3)]
Button(ButtonNode),
#[cultcache(key = 4)]
Toggle(ToggleNode),
#[cultcache(key = 5)]
Slider(SliderNode),
#[cultcache(key = 6)]
TextInput(TextInputNode),
#[cultcache(key = 7)]
Select(SelectNode),
#[cultcache(key = 8)]
Repeat(RepeatNode),
#[cultcache(key = 9)]
Overlay(OverlayNode),
#[cultcache(key = 10)]
Plot(PlotNode),
#[cultcache(key = 11)]
Embed(EmbedNode),
#[cultcache(key = 12)]
Use(UseNode),
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct BoxNode {
#[cultcache(key = 0)]
pub common: NodeCommon,
#[cultcache(key = 1)]
pub style: BoxStyle,
#[cultcache(key = 2)]
pub paint: Paint,
#[cultcache(key = 3)]
pub role: Role,
#[cultcache(key = 4)]
pub children: Vec<Node>,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct TextNode {
#[cultcache(key = 0)]
pub common: NodeCommon,
#[cultcache(key = 1)]
pub content: TextSource,
#[cultcache(key = 2)]
pub style_token: String,
#[cultcache(key = 3)]
pub wrap: bool,
#[cultcache(key = 4)]
pub max_lines: Option<i32>,
#[cultcache(key = 5)]
pub selectable: bool,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum ImageFit {
#[cultcache(key = 0)]
Contain,
#[cultcache(key = 1)]
Cover,
#[cultcache(key = 2)]
Fill,
#[cultcache(key = 3)]
Unscaled,
}
/// An asset of a catalog, with the alternative text a reader without the picture needs.
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct ImageNode {
#[cultcache(key = 0)]
pub common: NodeCommon,
#[cultcache(key = 1)]
pub catalog: String,
#[cultcache(key = 2)]
pub asset: String,
#[cultcache(key = 3)]
pub fit: ImageFit,
#[cultcache(key = 4)]
pub alt: TextSource,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct ButtonNode {
#[cultcache(key = 0)]
pub common: NodeCommon,
#[cultcache(key = 1)]
pub content: Vec<Node>,
#[cultcache(key = 2)]
pub on_activate: CommandRef,
#[cultcache(key = 3)]
pub enabled: BoolSource,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct ToggleNode {
#[cultcache(key = 0)]
pub common: NodeCommon,
#[cultcache(key = 1)]
pub checked: BoolSource,
#[cultcache(key = 2)]
pub on_change: CommandRef,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct SliderNode {
#[cultcache(key = 0)]
pub common: NodeCommon,
#[cultcache(key = 1)]
pub min: f64,
#[cultcache(key = 2)]
pub max: f64,
#[cultcache(key = 3)]
pub step: f64,
#[cultcache(key = 4)]
pub value: NumberSource,
#[cultcache(key = 5)]
pub commit: CommandRef,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum InputKind {
#[cultcache(key = 0)]
Text,
#[cultcache(key = 1)]
Number,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct TextInputNode {
#[cultcache(key = 0)]
pub common: NodeCommon,
#[cultcache(key = 1)]
pub multiline: bool,
#[cultcache(key = 2)]
pub kind: InputKind,
#[cultcache(key = 3)]
pub placeholder: TextSource,
#[cultcache(key = 4)]
pub initial: TextSource,
#[cultcache(key = 5)]
pub submit: CommandRef,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct SelectOption {
#[cultcache(key = 0)]
pub value: String,
#[cultcache(key = 1)]
pub label: String,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct LiteralOptions {
#[cultcache(key = 0)]
pub options: Vec<SelectOption>,
}
/// Options read from a collection: each item's value and label are members of the item.
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct BoundOptions {
#[cultcache(key = 0)]
pub collection: CollectionSource,
#[cultcache(key = 1)]
pub value_member: MemberPath,
#[cultcache(key = 2)]
pub label_member: MemberPath,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum OptionList {
#[cultcache(key = 0)]
Literal(LiteralOptions),
#[cultcache(key = 1)]
Bound(BoundOptions),
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct SelectNode {
#[cultcache(key = 0)]
pub common: NodeCommon,
#[cultcache(key = 1)]
pub options: OptionList,
#[cultcache(key = 2)]
pub value: Bind,
#[cultcache(key = 3)]
pub on_change: CommandRef,
}
/// The template is instantiated once per item of the collection, keyed by `key_member` of the item.
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct RepeatNode {
#[cultcache(key = 0)]
pub common: NodeCommon,
#[cultcache(key = 1)]
pub collection: CollectionSource,
#[cultcache(key = 2)]
pub key_member: MemberPath,
#[cultcache(key = 3)]
pub template: std::boxed::Box<Node>,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct AnchorNode {
#[cultcache(key = 0)]
pub node: NodeId,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum Anchor {
#[cultcache(key = 0)]
Viewport,
#[cultcache(key = 1)]
Node(AnchorNode),
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct OverlayNode {
#[cultcache(key = 0)]
pub common: NodeCommon,
#[cultcache(key = 1)]
pub anchor: Anchor,
#[cultcache(key = 2)]
pub modal: bool,
#[cultcache(key = 3)]
pub dismiss: CommandRef,
#[cultcache(key = 4)]
pub children: Vec<Node>,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum PlotMark {
#[cultcache(key = 0)]
Line,
#[cultcache(key = 1)]
Bar,
#[cultcache(key = 2)]
Area,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct PlotNode {
#[cultcache(key = 0)]
pub common: NodeCommon,
/// A numeric list member of a record.
#[cultcache(key = 1)]
pub series: Bind,
#[cultcache(key = 2)]
pub mark: PlotMark,
#[cultcache(key = 3)]
pub x_min: Option<f64>,
#[cultcache(key = 4)]
pub x_max: Option<f64>,
#[cultcache(key = 5)]
pub y_min: Option<f64>,
#[cultcache(key = 6)]
pub y_max: Option<f64>,
}
/// A leaf the host draws: `slot_kind` is a name the host declares it supports.
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct EmbedNode {
#[cultcache(key = 0)]
pub common: NodeCommon,
#[cultcache(key = 1)]
pub slot_kind: String,
#[cultcache(key = 2)]
pub slot_id: String,
#[cultcache(key = 3)]
pub width: f64,
#[cultcache(key = 4)]
pub height: f64,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct CompositionRef {
#[cultcache(key = 0)]
pub publisher: String,
#[cultcache(key = 1)]
pub name: String,
#[cultcache(key = 2)]
pub version: i32,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct UseNode {
#[cultcache(key = 0)]
pub common: NodeCommon,
#[cultcache(key = 1)]
pub composition: CompositionRef,
#[cultcache(key = 2)]
pub args: Vec<Arg>,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct Arg {
#[cultcache(key = 0)]
pub name: String,
#[cultcache(key = 1)]
pub value: ArgValue,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct TextArg {
#[cultcache(key = 0)]
pub value: String,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct NumberArg {
#[cultcache(key = 0)]
pub value: f64,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct BoolArg {
#[cultcache(key = 0)]
pub value: bool,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct CollectionArg {
#[cultcache(key = 0)]
pub source: CollectionSource,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct CommandArg {
#[cultcache(key = 0)]
pub command: CommandRef,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct SlotArg {
#[cultcache(key = 0)]
pub nodes: Vec<Node>,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum ArgValue {
#[cultcache(key = 0)]
Text(TextArg),
#[cultcache(key = 1)]
Number(NumberArg),
#[cultcache(key = 2)]
Bool(BoolArg),
#[cultcache(key = 3)]
Bind(Bind),
#[cultcache(key = 4)]
Collection(CollectionArg),
#[cultcache(key = 5)]
Command(CommandArg),
#[cultcache(key = 6)]
Slot(SlotArg),
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum ParamKind {
#[cultcache(key = 0)]
Text,
#[cultcache(key = 1)]
Number,
#[cultcache(key = 2)]
Bool,
#[cultcache(key = 3)]
Bind,
#[cultcache(key = 4)]
Collection,
#[cultcache(key = 5)]
Command,
#[cultcache(key = 6)]
Slot,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct ParamDecl {
#[cultcache(key = 0)]
pub name: String,
#[cultcache(key = 1)]
pub kind: ParamKind,
}
/// A provider's surface: a tree of nodes and the commands it declares. A change of structure
/// republishes the record with a higher `version`.
#[derive(Clone, Debug, PartialEq, DatabaseEntry)]
#[cultcache(type = "gamecult.thing.surface", schema = "gamecult.thing.surface.v1")]
pub struct SurfaceDocument {
#[cultcache(key = 0)]
pub provider_id: ProviderId,
#[cultcache(key = 1)]
pub label: String,
#[cultcache(key = 2)]
pub version: i64,
#[cultcache(key = 3)]
pub title: String,
#[cultcache(key = 4)]
pub root: Node,
#[cultcache(key = 5)]
pub commands: Vec<CommandDecl>,
}
impl SurfaceDocument {
pub fn record_key(&self) -> String {
surface_key(&self.provider_id, &self.label)
}
}
/// A reusable subtree with typed parameters. Compositions never own commands.
#[derive(Clone, Debug, PartialEq, DatabaseEntry)]
#[cultcache(type = "gamecult.thing.composition", schema = "gamecult.thing.composition.v1")]
pub struct CompositionDocument {
#[cultcache(key = 0)]
pub publisher: String,
#[cultcache(key = 1)]
pub name: String,
#[cultcache(key = 2)]
pub version: i32,
#[cultcache(key = 3)]
pub params: Vec<ParamDecl>,
#[cultcache(key = 4)]
pub body: Node,
}
impl CompositionDocument {
pub fn record_key(&self) -> String {
composition_key(&self.publisher, &self.name, self.version)
}
}

View file

@ -0,0 +1,185 @@
//! The answer to a command invocation. A `CommandResult` is built only by its four
//! constructors, each of which copies the invocation's id, and is read through accessors.
//! Nothing here takes a state as a string, and nothing builds a result from a map.
use cultcache_rs::{CultSlots, CultUnion, DatabaseEntry};
use crate::command::{CommandInvocation, CommandRef};
use crate::keys::{NodeId, result_key};
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct PendingOutcome {
#[cultcache(key = 0)]
pub message: String,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct CompletedOutcome {
#[cultcache(key = 0)]
pub source_version: Option<i64>,
/// Whether the captured drafts of the invocation clear.
#[cultcache(key = 1)]
pub clear_drafts: bool,
#[cultcache(key = 2)]
pub effect: Option<Effect>,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct DeniedOutcome {
#[cultcache(key = 0)]
pub reason: String,
#[cultcache(key = 1)]
pub node: Option<NodeId>,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct FailedOutcome {
#[cultcache(key = 0)]
pub reason: String,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum Outcome {
#[cultcache(key = 0)]
Pending(PendingOutcome),
#[cultcache(key = 1)]
Completed(CompletedOutcome),
#[cultcache(key = 2)]
Denied(DeniedOutcome),
#[cultcache(key = 3)]
Failed(FailedOutcome),
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct OpenSurfaceEffect {
#[cultcache(key = 0)]
pub key: String,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct OpenExternalEffect {
#[cultcache(key = 0)]
pub url: String,
/// The command run when the external step returns.
#[cultcache(key = 1)]
pub resume: CommandRef,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum Effect {
#[cultcache(key = 0)]
OpenSurface(OpenSurfaceEffect),
#[cultcache(key = 1)]
OpenExternal(OpenExternalEffect),
}
/// A provider's answer to one invocation. `seq` counts from 1 per invocation.
///
/// The positive case, a result built by a constructor from the invocation it answers:
///
/// ```
/// use thing_contract::{CommandInvocation, CommandResult};
/// fn answer(invocation: &CommandInvocation) -> CommandResult {
/// CommandResult::failed(invocation, 1, "refused")
/// }
/// ```
///
/// A struct literal does not compile, because the fields are private:
///
/// ```compile_fail,E0451
/// use thing_contract::{CommandResult, Outcome, FailedOutcome};
/// let _ = CommandResult {
/// invocation_id: String::new(),
/// seq: 1,
/// outcome: Outcome::Failed(FailedOutcome { reason: String::new() }),
/// };
/// ```
///
/// Neither does assigning the outcome:
///
/// ```compile_fail,E0616
/// use thing_contract::{CommandInvocation, CommandResult, Outcome, FailedOutcome};
/// fn tamper(invocation: &CommandInvocation) {
/// let mut result = CommandResult::failed(invocation, 1, "refused");
/// result.outcome = Outcome::Failed(FailedOutcome { reason: String::new() });
/// }
/// ```
///
/// Nothing converts a map into a result:
///
/// ```compile_fail,E0277
/// use std::collections::BTreeMap;
/// use thing_contract::CommandResult;
/// let flat: BTreeMap<String, String> = BTreeMap::new();
/// let _ = CommandResult::from(flat);
/// ```
///
/// Nor a string state:
///
/// ```compile_fail,E0277
/// use thing_contract::CommandResult;
/// let _ = CommandResult::from("completed");
/// ```
#[derive(Clone, Debug, PartialEq, DatabaseEntry)]
#[cultcache(type = "gamecult.thing.command_result", schema = "gamecult.thing.command_result.v1")]
pub struct CommandResult {
#[cultcache(key = 0)]
invocation_id: String,
#[cultcache(key = 1)]
seq: i32,
#[cultcache(key = 2)]
outcome: Outcome,
}
impl CommandResult {
fn answering(invocation: &CommandInvocation, seq: i32, outcome: Outcome) -> Self {
Self { invocation_id: invocation.invocation_id.clone(), seq, outcome }
}
pub fn pending(invocation: &CommandInvocation, seq: i32, message: impl Into<String>) -> Self {
Self::answering(invocation, seq, Outcome::Pending(PendingOutcome { message: message.into() }))
}
pub fn completed(
invocation: &CommandInvocation,
seq: i32,
source_version: Option<i64>,
clear_drafts: bool,
effect: Option<Effect>,
) -> Self {
Self::answering(
invocation,
seq,
Outcome::Completed(CompletedOutcome { source_version, clear_drafts, effect }),
)
}
pub fn denied(
invocation: &CommandInvocation,
seq: i32,
reason: impl Into<String>,
node: Option<NodeId>,
) -> Self {
Self::answering(invocation, seq, Outcome::Denied(DeniedOutcome { reason: reason.into(), node }))
}
pub fn failed(invocation: &CommandInvocation, seq: i32, reason: impl Into<String>) -> Self {
Self::answering(invocation, seq, Outcome::Failed(FailedOutcome { reason: reason.into() }))
}
pub fn invocation_id(&self) -> &str {
&self.invocation_id
}
pub fn seq(&self) -> i32 {
self.seq
}
pub fn outcome(&self) -> &Outcome {
&self.outcome
}
pub fn record_key(&self) -> String {
result_key(&self.invocation_id, self.seq)
}
}

View file

@ -0,0 +1,169 @@
//! Where a value comes from: a literal, a binding to a state record, or a composition parameter.
//! A member path is a list of names and indices, never an expression.
use cultcache_rs::{CultSlots, CultUnion};
use crate::keys::{ProviderId, RecordKey, SchemaId};
/// A composition parameter, named.
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct ParamName {
#[cultcache(key = 0)]
pub name: String,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct TextLiteral {
#[cultcache(key = 0)]
pub value: String,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct NumberLiteral {
#[cultcache(key = 0)]
pub value: f64,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct BoolLiteral {
#[cultcache(key = 0)]
pub value: bool,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum TextSource {
#[cultcache(key = 0)]
Literal(TextLiteral),
#[cultcache(key = 1)]
Bound(Bind),
#[cultcache(key = 2)]
Param(ParamName),
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum NumberSource {
#[cultcache(key = 0)]
Literal(NumberLiteral),
#[cultcache(key = 1)]
Bound(Bind),
#[cultcache(key = 2)]
Param(ParamName),
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum BoolSource {
#[cultcache(key = 0)]
Literal(BoolLiteral),
#[cultcache(key = 1)]
Bound(Bind),
#[cultcache(key = 2)]
Param(ParamName),
}
/// One member of a record, read through a path, optionally formatted.
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct Bind {
#[cultcache(key = 0)]
pub source: StateSource,
#[cultcache(key = 1)]
pub member: MemberPath,
#[cultcache(key = 2)]
pub format: Option<Format>,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum StateSource {
#[cultcache(key = 0)]
Record(StateRef),
/// The current item inside a `Repeat` template.
#[cultcache(key = 1)]
Item,
}
/// A state record. An absent provider is the publisher of the document.
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct StateRef {
#[cultcache(key = 0)]
pub provider: Option<ProviderId>,
#[cultcache(key = 1)]
pub schema: SchemaId,
#[cultcache(key = 2)]
pub key: RecordKey,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct MemberPath {
#[cultcache(key = 0)]
pub steps: Vec<MemberStep>,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum MemberStep {
#[cultcache(key = 0)]
Name(MemberName),
#[cultcache(key = 1)]
Index(MemberIndex),
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct MemberName {
#[cultcache(key = 0)]
pub name: String,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct MemberIndex {
#[cultcache(key = 0)]
pub index: i32,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum Format {
#[cultcache(key = 0)]
Number(NumberFormat),
#[cultcache(key = 1)]
Duration,
#[cultcache(key = 2)]
Timestamp,
#[cultcache(key = 3)]
Affix(AffixFormat),
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct NumberFormat {
#[cultcache(key = 0)]
pub precision: i32,
#[cultcache(key = 1)]
pub percent: bool,
#[cultcache(key = 2)]
pub unit: Option<String>,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct AffixFormat {
#[cultcache(key = 0)]
pub prefix: String,
#[cultcache(key = 1)]
pub suffix: String,
}
/// The records of one type, or a list member of one record.
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum CollectionSource {
#[cultcache(key = 0)]
Records(RecordsSource),
#[cultcache(key = 1)]
Member(Bind),
#[cultcache(key = 2)]
Param(ParamName),
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct RecordsSource {
#[cultcache(key = 0)]
pub provider: Option<ProviderId>,
#[cultcache(key = 1)]
pub schema: SchemaId,
#[cultcache(key = 2)]
pub key_prefix: String,
}

View file

@ -0,0 +1,241 @@
//! Box layout and paint as plain data. The crate holds no layout engine: core maps `BoxStyle`
//! onto its solver.
use cultcache_rs::{CultSlots, CultUnion};
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct Scalar {
#[cultcache(key = 0)]
pub value: f64,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum Dimension {
#[cultcache(key = 0)]
Px(Scalar),
#[cultcache(key = 1)]
Percent(Scalar),
#[cultcache(key = 2)]
Fr(Scalar),
#[cultcache(key = 3)]
Auto,
#[cultcache(key = 4)]
Content,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct Size2 {
#[cultcache(key = 0)]
pub width: Dimension,
#[cultcache(key = 1)]
pub height: Dimension,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct Edges {
#[cultcache(key = 0)]
pub top: Dimension,
#[cultcache(key = 1)]
pub right: Dimension,
#[cultcache(key = 2)]
pub bottom: Dimension,
#[cultcache(key = 3)]
pub left: Dimension,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum Display {
#[cultcache(key = 0)]
Flex,
#[cultcache(key = 1)]
Grid,
#[cultcache(key = 2)]
Block,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum Direction {
#[cultcache(key = 0)]
Row,
#[cultcache(key = 1)]
Column,
#[cultcache(key = 2)]
RowReverse,
#[cultcache(key = 3)]
ColumnReverse,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum Wrap {
#[cultcache(key = 0)]
NoWrap,
#[cultcache(key = 1)]
Wrap,
#[cultcache(key = 2)]
WrapReverse,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum Position {
#[cultcache(key = 0)]
Relative,
#[cultcache(key = 1)]
Absolute,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum Overflow {
#[cultcache(key = 0)]
Visible,
#[cultcache(key = 1)]
Clip,
#[cultcache(key = 2)]
Scroll,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum ItemAlign {
#[cultcache(key = 0)]
Auto,
#[cultcache(key = 1)]
Start,
#[cultcache(key = 2)]
End,
#[cultcache(key = 3)]
Center,
#[cultcache(key = 4)]
Stretch,
#[cultcache(key = 5)]
Baseline,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum ContentAlign {
#[cultcache(key = 0)]
Auto,
#[cultcache(key = 1)]
Start,
#[cultcache(key = 2)]
End,
#[cultcache(key = 3)]
Center,
#[cultcache(key = 4)]
Stretch,
#[cultcache(key = 5)]
SpaceBetween,
#[cultcache(key = 6)]
SpaceAround,
#[cultcache(key = 7)]
SpaceEvenly,
}
/// The layout of a box. `gap` is the column gap in `width` and the row gap in `height`.
/// `grid_columns` and `grid_rows` are the grid tracks and are read only when `display` is Grid.
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct BoxStyle {
#[cultcache(key = 0)]
pub display: Display,
#[cultcache(key = 1)]
pub direction: Direction,
#[cultcache(key = 2)]
pub wrap: Wrap,
#[cultcache(key = 3)]
pub gap: Size2,
#[cultcache(key = 4)]
pub padding: Edges,
#[cultcache(key = 5)]
pub margin: Edges,
#[cultcache(key = 6)]
pub size: Size2,
#[cultcache(key = 7)]
pub min_size: Size2,
#[cultcache(key = 8)]
pub max_size: Size2,
#[cultcache(key = 9)]
pub position: Position,
#[cultcache(key = 10)]
pub inset: Edges,
#[cultcache(key = 11)]
pub overflow: Overflow,
#[cultcache(key = 12)]
pub align_items: ItemAlign,
#[cultcache(key = 13)]
pub align_self: ItemAlign,
#[cultcache(key = 14)]
pub align_content: ContentAlign,
#[cultcache(key = 15)]
pub justify_content: ContentAlign,
#[cultcache(key = 16)]
pub grow: f64,
#[cultcache(key = 17)]
pub shrink: f64,
#[cultcache(key = 18)]
pub basis: Dimension,
#[cultcache(key = 19)]
pub grid_columns: Vec<Dimension>,
#[cultcache(key = 20)]
pub grid_rows: Vec<Dimension>,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct ThemeToken {
#[cultcache(key = 0)]
pub name: String,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct Rgba {
#[cultcache(key = 0)]
pub r: u8,
#[cultcache(key = 1)]
pub g: u8,
#[cultcache(key = 2)]
pub b: u8,
#[cultcache(key = 3)]
pub a: u8,
}
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum ColorRef {
#[cultcache(key = 0)]
Token(ThemeToken),
#[cultcache(key = 1)]
Rgba(Rgba),
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct Border {
#[cultcache(key = 0)]
pub color: ColorRef,
#[cultcache(key = 1)]
pub width: f64,
}
#[derive(Clone, Debug, PartialEq, CultSlots)]
pub struct Paint {
#[cultcache(key = 0)]
pub background: Option<ColorRef>,
#[cultcache(key = 1)]
pub border: Option<Border>,
#[cultcache(key = 2)]
pub radius: f64,
}
/// The accessibility role of a box. The accessibility tree itself is core's.
#[derive(Clone, Debug, PartialEq, CultUnion)]
pub enum Role {
#[cultcache(key = 0)]
Group,
#[cultcache(key = 1)]
Region,
#[cultcache(key = 2)]
List,
#[cultcache(key = 3)]
ListItem,
#[cultcache(key = 4)]
Heading,
#[cultcache(key = 5)]
Dialog,
#[cultcache(key = 6)]
Toolbar,
}