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}