1use std::collections::HashSet;
2
3#[derive(Debug, Clone, Default)]
5pub struct Table {
6 pub schema: String,
7 pub name: String,
8 pub columns: Vec<Column>,
9 pub primary_keys: Vec<String>,
10 pub foreign_keys: Vec<ForeignKey>,
11 pub not_null_cols: HashSet<String>,
12 pub unique_constraints: Vec<UniqueConstraint>,
13 pub check_constraints: Vec<CheckConstraint>,
14 pub indexes: Vec<IndexInfo>,
15}
16
17impl Table {
18 #[must_use]
20 pub fn key(&self) -> (&str, &str) {
21 (&self.schema, &self.name)
22 }
23}
24
25#[derive(Debug, Clone)]
27pub struct Column {
28 pub name: String,
29 pub data_type: String,
30}
31
32#[derive(Debug, Clone)]
34pub struct ForeignKey {
35 pub name: String,
36 pub from_columns: Vec<String>,
37 pub to_schema: String,
38 pub to_table: String,
39 pub to_columns: Vec<String>,
40}
41
42#[derive(Debug, Clone)]
44pub struct UniqueConstraint {
45 pub name: String,
46 pub columns: Vec<String>,
47}
48
49#[derive(Debug, Clone)]
51pub struct CheckConstraint {
52 pub name: String,
53 pub definition: String,
54}
55
56#[derive(Debug, Clone)]
58pub struct IndexInfo {
59 pub name: String,
60 pub columns: Vec<String>,
61 pub is_unique: bool,
62}
63
64pub const SYSTEM_SCHEMAS: [&str; 2] = ["information_schema", "pg_catalog"];
66
67#[must_use]
75pub fn filter_tables(
76 tables: Vec<Table>,
77 schemas: &[&str],
78 tables_filter: &[&str],
79 ignore_tables: &[&str],
80) -> Vec<Table> {
81 tables
82 .into_iter()
83 .filter(|t| {
84 let schema_ok = if schemas.is_empty() {
85 !SYSTEM_SCHEMAS.contains(&t.schema.as_str())
86 } else {
87 schemas.contains(&t.schema.as_str())
88 };
89
90 let table_ok = tables_filter.is_empty() || tables_filter.contains(&t.name.as_str());
91 let not_ignored = !ignore_tables.contains(&t.name.as_str());
92
93 schema_ok && table_ok && not_ignored
94 })
95 .collect()
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 fn table(schema: &str, name: &str) -> Table {
103 Table {
104 schema: schema.to_string(),
105 name: name.to_string(),
106 ..Table::default()
107 }
108 }
109
110 #[test]
111 fn filter_tables_excludes_system_schemas_by_default() {
112 let tables = vec![
113 table("public", "users"),
114 table("pg_catalog", "pg_class"),
115 table("information_schema", "columns"),
116 ];
117
118 let result = filter_tables(tables, &[], &[], &[]);
119
120 assert_eq!(result.len(), 1);
121 assert_eq!(result[0].name, "users");
122 }
123
124 #[test]
125 fn filter_tables_keeps_only_requested_schemas() {
126 let tables = vec![table("public", "users"), table("extended", "audit")];
127
128 let result = filter_tables(tables, &["extended"], &[], &[]);
129
130 assert_eq!(result.len(), 1);
131 assert_eq!(result[0].schema, "extended");
132 }
133
134 #[test]
135 fn filter_tables_combines_schema_and_table_filters() {
136 let tables = vec![
137 table("public", "users"),
138 table("public", "orders"),
139 table("extended", "users"),
140 ];
141
142 let result = filter_tables(tables, &["public"], &["users"], &[]);
143
144 assert_eq!(result.len(), 1);
145 assert_eq!(result[0].key(), ("public", "users"));
146 }
147
148 #[test]
149 fn filter_tables_excludes_ignored_tables() {
150 let tables = vec![
151 table("public", "users"),
152 table("public", "logs"),
153 table("extended", "logs"),
154 ];
155
156 let result = filter_tables(tables, &[], &[], &["logs"]);
157
158 assert_eq!(result.len(), 1);
159 assert_eq!(result[0].name, "users");
160 }
161
162 #[test]
163 fn filter_tables_combines_schema_and_ignore_filters() {
164 let tables = vec![
165 table("public", "users"),
166 table("public", "logs"),
167 table("extended", "logs"),
168 ];
169
170 let result = filter_tables(tables, &["public"], &[], &["logs"]);
171
172 assert_eq!(result.len(), 1);
173 assert_eq!(result[0].key(), ("public", "users"));
174 }
175}