Skip to main content

spacedb_sdk/
schema.rs

1//! The schema — every field declares its **CRDT type** (how it merges) and its
2//! **consistency tier** (how strong a guarantee it carries). This is the single
3//! place a developer encodes "this is a counter that auto-merges" vs "this username
4//! must be globally unique", and the rest of the SDK routes accordingly.
5
6use std::collections::HashMap;
7
8use spacedb_consistency::Tier;
9
10/// The CRDT a field is represented by — its merge semantics.
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum CrdtType {
13    /// Last-writer-wins register (a scalar value).
14    Register,
15    /// PN-counter (add/subtract, commutes).
16    Counter,
17    /// Collaborative text.
18    Text,
19    /// Add-wins observed-remove set.
20    Set,
21}
22
23impl CrdtType {
24    pub fn name(&self) -> &'static str {
25        match self {
26            CrdtType::Register => "register",
27            CrdtType::Counter => "counter",
28            CrdtType::Text => "text",
29            CrdtType::Set => "set",
30        }
31    }
32}
33
34/// A field's representation and guarantee.
35#[derive(Clone, Copy, Debug, PartialEq, Eq)]
36pub struct FieldSpec {
37    pub crdt: CrdtType,
38    pub tier: Tier,
39}
40
41/// The fields of a collection.
42#[derive(Clone, Debug)]
43pub struct Schema {
44    collection: String,
45    fields: HashMap<String, FieldSpec>,
46}
47
48impl Schema {
49    pub fn new(collection: impl Into<String>) -> Self {
50        Self {
51            collection: collection.into(),
52            fields: HashMap::new(),
53        }
54    }
55
56    /// Declare a field (builder style). A `Register`/`Counter`/`Text`/`Set` at a
57    /// `Convergent`/`Causal`/`Strong` tier.
58    pub fn field(mut self, name: impl Into<String>, crdt: CrdtType, tier: Tier) -> Self {
59        self.fields.insert(name.into(), FieldSpec { crdt, tier });
60        self
61    }
62
63    pub fn collection(&self) -> &str {
64        &self.collection
65    }
66
67    pub fn spec(&self, field: &str) -> Option<FieldSpec> {
68        self.fields.get(field).copied()
69    }
70}