Skip to main content

contextdb_core/
table_meta.rs

1use crate::Direction;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5#[derive(Debug, Clone, Default, Serialize, Deserialize)]
6pub struct TableMeta {
7    pub columns: Vec<ColumnDef>,
8    pub immutable: bool,
9    pub state_machine: Option<StateMachineConstraint>,
10    #[serde(default)]
11    pub dag_edge_types: Vec<String>,
12    pub natural_key_column: Option<String>,
13    #[serde(default)]
14    pub propagation_rules: Vec<PropagationRule>,
15    #[serde(default)]
16    pub default_ttl_seconds: Option<u64>,
17    #[serde(default)]
18    pub sync_safe: bool,
19    #[serde(default)]
20    pub expires_column: Option<String>,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub enum PropagationRule {
25    ForeignKey {
26        fk_column: String,
27        referenced_table: String,
28        referenced_column: String,
29        trigger_state: String,
30        target_state: String,
31        max_depth: u32,
32        abort_on_failure: bool,
33    },
34    Edge {
35        edge_type: String,
36        direction: Direction,
37        trigger_state: String,
38        target_state: String,
39        max_depth: u32,
40        abort_on_failure: bool,
41    },
42    VectorExclusion {
43        trigger_state: String,
44    },
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct StateMachineConstraint {
49    pub column: String,
50    pub transitions: HashMap<String, Vec<String>>,
51}
52
53#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
54pub struct ColumnDef {
55    pub name: String,
56    pub column_type: ColumnType,
57    pub nullable: bool,
58    pub primary_key: bool,
59    #[serde(default)]
60    pub unique: bool,
61    #[serde(default)]
62    pub default: Option<String>,
63    #[serde(default)]
64    pub expires: bool,
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
68pub enum ColumnType {
69    Integer,
70    Real,
71    Text,
72    Boolean,
73    Json,
74    Uuid,
75    Vector(usize),
76    Timestamp,
77}
78
79impl TableMeta {
80    pub fn estimated_bytes(&self) -> usize {
81        let columns_bytes = self.columns.iter().fold(0usize, |acc, column| {
82            acc.saturating_add(column.estimated_bytes())
83        });
84        let state_machine_bytes = self
85            .state_machine
86            .as_ref()
87            .map(StateMachineConstraint::estimated_bytes)
88            .unwrap_or(0);
89        let dag_bytes = self.dag_edge_types.iter().fold(0usize, |acc, edge_type| {
90            acc.saturating_add(32 + edge_type.len() * 16)
91        });
92        let natural_key_bytes = self
93            .natural_key_column
94            .as_ref()
95            .map(|column| 32 + column.len() * 16)
96            .unwrap_or(0);
97        let propagation_bytes = self.propagation_rules.iter().fold(0usize, |acc, rule| {
98            acc.saturating_add(rule.estimated_bytes())
99        });
100        let expires_bytes = self
101            .expires_column
102            .as_ref()
103            .map(|column| 32 + column.len() * 16)
104            .unwrap_or(0);
105
106        16 + columns_bytes
107            + state_machine_bytes
108            + dag_bytes
109            + natural_key_bytes
110            + propagation_bytes
111            + expires_bytes
112            + self.default_ttl_seconds.map(|_| 8).unwrap_or(0)
113            + 8
114    }
115}
116
117impl PropagationRule {
118    fn estimated_bytes(&self) -> usize {
119        match self {
120            PropagationRule::ForeignKey {
121                fk_column,
122                referenced_table,
123                referenced_column,
124                trigger_state,
125                target_state,
126                ..
127            } => {
128                24 + fk_column.len() * 16
129                    + referenced_table.len() * 16
130                    + referenced_column.len() * 16
131                    + trigger_state.len() * 16
132                    + target_state.len() * 16
133            }
134            PropagationRule::Edge {
135                edge_type,
136                trigger_state,
137                target_state,
138                ..
139            } => 24 + edge_type.len() * 16 + trigger_state.len() * 16 + target_state.len() * 16,
140            PropagationRule::VectorExclusion { trigger_state } => 16 + trigger_state.len() * 16,
141        }
142    }
143}
144
145impl StateMachineConstraint {
146    fn estimated_bytes(&self) -> usize {
147        let transitions_bytes = self.transitions.iter().fold(0usize, |acc, (from, tos)| {
148            acc.saturating_add(
149                32 + from.len() * 16 + tos.iter().map(|to| 16 + to.len() * 16).sum::<usize>(),
150            )
151        });
152        24 + self.column.len() * 16 + transitions_bytes
153    }
154}
155
156impl ColumnDef {
157    fn estimated_bytes(&self) -> usize {
158        let default_bytes = self
159            .default
160            .as_ref()
161            .map(|value| 32 + value.len() * 16)
162            .unwrap_or(0);
163        8 + self.name.len() * 16 + self.column_type.estimated_bytes() + default_bytes + 8
164    }
165}
166
167impl ColumnType {
168    fn estimated_bytes(&self) -> usize {
169        match self {
170            ColumnType::Integer => 16,
171            ColumnType::Real => 16,
172            ColumnType::Text => 16,
173            ColumnType::Boolean => 16,
174            ColumnType::Json => 24,
175            ColumnType::Uuid => 16,
176            ColumnType::Vector(_) => 24,
177            ColumnType::Timestamp => 16,
178        }
179    }
180}