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:
parent
d8f5e1f2fe
commit
852c34b60a
12 changed files with 1933 additions and 0 deletions
14
crates/thing-contract/Cargo.toml
Normal file
14
crates/thing-contract/Cargo.toml
Normal 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 }
|
||||
38
crates/thing-contract/src/asset.rs
Normal file
38
crates/thing-contract/src/asset.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
120
crates/thing-contract/src/command.rs
Normal file
120
crates/thing-contract/src/command.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
63
crates/thing-contract/src/keys.rs
Normal file
63
crates/thing-contract/src/keys.rs
Normal 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)
|
||||
}
|
||||
37
crates/thing-contract/src/lib.rs
Normal file
37
crates/thing-contract/src/lib.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
454
crates/thing-contract/src/node.rs
Normal file
454
crates/thing-contract/src/node.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
185
crates/thing-contract/src/result.rs
Normal file
185
crates/thing-contract/src/result.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
169
crates/thing-contract/src/source.rs
Normal file
169
crates/thing-contract/src/source.rs
Normal 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,
|
||||
}
|
||||
241
crates/thing-contract/src/style.rs
Normal file
241
crates/thing-contract/src/style.rs
Normal 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,
|
||||
}
|
||||
Loading…
Reference in a new issue