Skip to main content

catcolab_document_types/v2/
instance.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3use tsify::Tsify;
4use uuid::Uuid;
5
6/// The value of a single "cell" (i.e. field) in a table row. If the column corresponds to an
7/// attribute morphism then we provide the value of the type; if the column corresponds to a
8/// mapping morphism then we provide the uuid of the row.
9#[derive(PartialEq, Debug, Serialize, Deserialize, Tsify)]
10#[tsify(into_wasm_abi, from_wasm_abi)]
11pub enum FieldValue {
12    /// Base type: the empty type.
13    Null,
14    /// Base type: boolean.
15    Bool(bool),
16    /// Base type: integer.
17    Int(i32),
18    /// Base type: float.
19    Float(f32),
20    /// Base type: string.
21    String(String),
22    /// Mapping type: the uuid of another row.
23    RowRef(Uuid),
24}
25
26/// A single row of a table.
27#[derive(PartialEq, Debug, Serialize, Deserialize, Tsify)]
28#[tsify(into_wasm_abi, from_wasm_abi, hashmap_as_object)]
29pub struct TableRow {
30    /// The content of the row, given as a map from morphism `QualifiedName` to values.
31    pub fields: HashMap<String, FieldValue>,
32}
33
34/// A single table, corresponding to a single entity.
35#[derive(PartialEq, Debug, Serialize, Deserialize, Tsify)]
36#[tsify(into_wasm_abi, from_wasm_abi, hashmap_as_object)]
37pub struct Table {
38    /// The rows of the table.
39    pub rows: HashMap<Uuid, TableRow>,
40    /// The order of the rows of the table.
41    #[serde(rename = "rowOrder")]
42    pub row_order: Vec<Uuid>,
43}
44
45#[cfg(test)]
46mod test {
47    use super::*;
48    use serde_json::Value;
49
50    #[test]
51    fn tables_are_keyed_by_schema_entity_id_in_json() {
52        let row_id = Uuid::from_u128(1);
53        let col_id = Uuid::from_u128(2);
54        let ent_id = Uuid::from_u128(3).to_string();
55
56        let mut fields = HashMap::new();
57        fields.insert(col_id.to_string(), FieldValue::Int(42));
58
59        let mut rows = HashMap::new();
60        rows.insert(row_id, TableRow { fields });
61
62        let table = Table { rows, row_order: vec![row_id] };
63
64        let mut tables = HashMap::new();
65        tables.insert(ent_id.clone(), table);
66
67        let value = serde_json::to_value(&tables).expect("serialize to JSON");
68
69        // Schema entity and row IDs must be plain strings in JSON objects.
70        let table_obj = value.get(&ent_id).and_then(Value::as_object).expect("table object");
71        let rows_obj = table_obj.get("rows").and_then(Value::as_object).expect("rows object");
72        assert!(rows_obj.contains_key(&row_id.to_string()));
73
74        let round_tripped: HashMap<String, Table> =
75            serde_json::from_value(value).expect("deserialize from JSON");
76        assert_eq!(round_tripped, tables);
77    }
78
79    #[test]
80    fn multi_segment_key_round_trips() {
81        let a = Uuid::from_u128(10);
82        let b = Uuid::from_u128(11);
83        let row_id = Uuid::from_u128(13);
84
85        // Paths of UUIDs are represented as dot-separated strings.
86        let key = format!("{a}.{b}");
87
88        let mut fields = HashMap::new();
89        fields.insert(key.clone(), FieldValue::RowRef(row_id));
90
91        let mut rows = HashMap::new();
92        rows.insert(row_id, TableRow { fields });
93
94        let table = Table { rows, row_order: vec![row_id] };
95
96        let value = serde_json::to_value(&table).expect("serialize to JSON");
97
98        let fields_obj = value
99            .pointer(&format!("/rows/{row_id}/fields"))
100            .and_then(Value::as_object)
101            .expect("fields object");
102        assert!(fields_obj.contains_key(&key));
103
104        let round_tripped: Table = serde_json::from_value(value).expect("deserialize from JSON");
105        assert_eq!(round_tripped, table);
106    }
107}