1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
4pub struct Table {
5 pub schema: String,
6 pub name: String,
7 pub columns: Vec<Column>,
8 pub primary_key: Vec<String>,
9 pub is_view: bool,
10}
11
12impl Table {
13 pub fn new(schema: impl Into<String>, name: impl Into<String>) -> Self {
14 Self {
15 schema: schema.into(),
16 name: name.into(),
17 columns: Vec::new(),
18 primary_key: Vec::new(),
19 is_view: false,
20 }
21 }
22
23 pub fn with_columns(mut self, columns: Vec<Column>) -> Self {
24 self.columns = columns;
25 self
26 }
27
28 pub fn with_primary_key(mut self, primary_key: Vec<String>) -> Self {
29 self.primary_key = primary_key;
30 self
31 }
32
33 pub fn as_view(mut self) -> Self {
34 self.is_view = true;
35 self
36 }
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
40pub struct Column {
41 pub name: String,
42 pub type_: String,
43 pub nullable: bool,
44 pub has_default: bool,
45 pub position: usize,
46}
47
48impl Column {
49 pub fn new(name: impl Into<String>, type_: impl Into<String>) -> Self {
50 Self {
51 name: name.into(),
52 type_: type_.into(),
53 nullable: false,
54 has_default: false,
55 position: 0,
56 }
57 }
58
59 pub fn with_position(mut self, position: usize) -> Self {
60 self.position = position;
61 self
62 }
63
64 pub fn nullable(mut self, nullable: bool) -> Self {
65 self.nullable = nullable;
66 self
67 }
68
69 pub fn with_default(mut self, has_default: bool) -> Self {
70 self.has_default = has_default;
71 self
72 }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
76#[serde(rename_all = "snake_case")]
77pub enum Cardinality {
78 ManyToOne,
79 OneToMany,
80 OneToOne,
81 ManyToMany,
82}
83
84#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
85pub struct Junction {
86 pub schema: String,
87 pub table: String,
88 pub source_columns: Vec<String>,
89 pub target_columns: Vec<String>,
90}
91
92#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
93pub struct Relationship {
94 pub constraint_name: String,
95 pub source_schema: String,
96 pub source_table: String,
97 pub source_columns: Vec<String>,
98 pub target_schema: String,
99 pub target_table: String,
100 pub target_columns: Vec<String>,
101 pub cardinality: Cardinality,
102 pub junction: Option<Junction>,
103}
104
105impl Relationship {
106 pub fn new(
107 constraint_name: impl Into<String>,
108 source_schema: impl Into<String>,
109 source_table: impl Into<String>,
110 target_schema: impl Into<String>,
111 target_table: impl Into<String>,
112 cardinality: Cardinality,
113 ) -> Self {
114 Self {
115 constraint_name: constraint_name.into(),
116 source_schema: source_schema.into(),
117 source_table: source_table.into(),
118 source_columns: Vec::new(),
119 target_schema: target_schema.into(),
120 target_table: target_table.into(),
121 target_columns: Vec::new(),
122 cardinality,
123 junction: None,
124 }
125 }
126
127 pub fn with_source_columns(mut self, columns: Vec<String>) -> Self {
128 self.source_columns = columns;
129 self
130 }
131
132 pub fn with_target_columns(mut self, columns: Vec<String>) -> Self {
133 self.target_columns = columns;
134 self
135 }
136
137 pub fn with_junction(mut self, junction: Junction) -> Self {
138 self.junction = Some(junction);
139 self
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn test_table_new() {
149 let table = Table::new("public", "users");
150 assert_eq!(table.schema, "public");
151 assert_eq!(table.name, "users");
152 }
153
154 #[test]
155 fn test_table_with_columns() {
156 let columns = vec![Column::new("id", "integer"), Column::new("name", "text")];
157 let table = Table::new("public", "users").with_columns(columns);
158 assert_eq!(table.columns.len(), 2);
159 }
160
161 #[test]
162 fn test_table_as_view() {
163 let table = Table::new("public", "user_stats").as_view();
164 assert!(table.is_view);
165 }
166
167 #[test]
168 fn test_column_new() {
169 let column = Column::new("id", "integer");
170 assert_eq!(column.name, "id");
171 assert_eq!(column.type_, "integer");
172 }
173
174 #[test]
175 fn test_relationship_new() {
176 let rel = Relationship::new(
177 "fk_user_client",
178 "public",
179 "users",
180 "public",
181 "clients",
182 Cardinality::ManyToOne,
183 );
184 assert_eq!(rel.source_table, "users");
185 assert_eq!(rel.target_table, "clients");
186 assert_eq!(rel.cardinality, Cardinality::ManyToOne);
187 }
188
189 #[test]
190 fn test_relationship_with_junction() {
191 let junction = Junction {
192 schema: "public".to_string(),
193 table: "post_tags".to_string(),
194 source_columns: vec!["post_id".to_string()],
195 target_columns: vec!["tag_id".to_string()],
196 };
197
198 let rel = Relationship::new(
199 "pk_post_tags",
200 "public",
201 "posts",
202 "public",
203 "tags",
204 Cardinality::ManyToMany,
205 )
206 .with_junction(junction);
207
208 assert!(rel.junction.is_some());
209 }
210
211 #[test]
212 fn test_table_serialization() {
213 let table = Table::new("public", "users");
214 let json = serde_json::to_string(&table).unwrap();
215 assert!(json.contains("users"));
216 }
217
218 #[test]
219 fn test_relationship_serialization() {
220 let rel = Relationship::new(
221 "fk",
222 "public",
223 "users",
224 "public",
225 "clients",
226 Cardinality::ManyToOne,
227 );
228 let json = serde_json::to_string(&rel).unwrap();
229 assert!(json.contains("many_to_one"));
230 }
231}