1use serde::Serialize;
2use std::collections::{BTreeMap, BTreeSet};
3use std::sync::Arc;
4use std::vec::Vec;
5
6#[derive(Debug, Clone, Serialize, PartialEq)]
7pub struct TableColumn {
8 pub name: String,
9 pub col_num: i16, pub datatype: String,
13 pub constraints: BTreeSet<ColumnConstraints>,
14}
15
16impl TableColumn {
17 pub fn is_pk(&self) -> bool {
18 self.constraints.contains(&ColumnConstraints::PrimaryKey)
19 }
20 pub fn is_fk(&self) -> bool {
21 self.constraints.contains(&ColumnConstraints::ForeignKey)
22 }
23 pub fn is_nn(&self) -> bool {
24 self.constraints.contains(&ColumnConstraints::NotNull)
25 }
26}
27
28#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
29pub enum ColumnConstraints {
30 NotNull,
31 PrimaryKey,
32 ForeignKey,
33 Unique,
34 Check(String), Default(String), Index, }
38
39#[derive(Clone, Debug)]
41pub struct ForeignKey {
42 pub source_table: Arc<Table>,
43 pub source_columns: Vec<Arc<TableColumn>>,
44 pub target_table: Arc<Table>,
45 pub target_columns: Vec<Arc<TableColumn>>,
46 pub is_zero_one_to_one: bool, }
48
49#[derive(Debug, Clone)]
50pub struct Table {
51 pub name: String,
52 pub columns: Vec<Arc<TableColumn>>,
53 pub has_composite_pk: bool,
54}
55
56#[derive(Clone, Debug, PartialEq)]
57pub struct View {
58 pub materialized: bool,
59 pub name: String,
60 pub columns: Vec<Arc<TableColumn>>,
61}
62
63pub type SqlEnums = BTreeMap<String, Vec<String>>;
65
66#[derive(Clone, Debug)]
68pub struct SqlERData {
69 pub tables: Vec<Arc<Table>>,
70 pub foreign_keys: Vec<ForeignKey>,
71 pub enums: SqlEnums,
72 pub views: Vec<Arc<View>>,
73}
74
75#[async_trait::async_trait]
76pub trait SqlERDataLoader {
77 async fn load_erd_data(&mut self) -> Result<SqlERData, crate::SqlantError>;
79}
80
81impl Table {
82 pub fn new(name: String, columns: Vec<Arc<TableColumn>>) -> Table {
83 let has_composite_pk = columns.iter().fold(0, |acc, x| {
84 acc + x .as_ref() .constraints
87 .contains(&ColumnConstraints::PrimaryKey) as u16
88 }) > 1; Table {
91 name,
92 columns,
93 has_composite_pk,
94 }
95 }
96}
97
98impl ForeignKey {
99 fn is_zero_one_to_one(
100 source_table: &Arc<Table>,
101 source_columns: &[Arc<TableColumn>],
102 target_table: &Arc<Table>,
103 target_columns: &[Arc<TableColumn>],
104 ) -> bool {
105 if source_columns.iter().any(|col| !col.is_pk()) {
106 return false;
107 }
108 if target_columns.iter().any(|col| !col.is_pk()) {
109 return false;
110 }
111
112 if source_table.columns.iter().filter(|c| c.is_pk()).count()
115 != target_table.columns.iter().filter(|c| c.is_pk()).count()
116 {
117 return false;
118 }
119
120 true
121 }
122
123 pub fn new(
124 source_table: Arc<Table>,
125 source_columns: Vec<Arc<TableColumn>>,
126 target_table: Arc<Table>,
127 target_columns: Vec<Arc<TableColumn>>,
128 ) -> ForeignKey {
129 let is_zero_one_to_one = Self::is_zero_one_to_one(
130 &source_table,
131 &source_columns,
132 &target_table,
133 &target_columns,
134 );
135 ForeignKey {
136 source_table,
137 source_columns,
138 target_table,
139 target_columns,
140 is_zero_one_to_one,
141 }
142 }
143}