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