Skip to main content

schema_model/model/
table.rs

1use crate::model::aggregation::Aggregation;
2use crate::model::column::Column;
3use crate::model::column_type::ColumnType;
4use crate::model::constraint::Constraint;
5use crate::model::initial_data::InitialData;
6use crate::model::key::Key;
7use crate::model::relation::Relation;
8use crate::model::trigger::Trigger;
9use crate::model::types::{BooleanMode, KeyType, LockEscalation, TableOption};
10use std::fmt;
11
12#[derive(Debug, Clone)]
13pub struct Table {
14    schema_name: Option<String>,
15    name: String,
16    export_date_column: Option<String>,
17    lock_escalation: LockEscalation,
18    no_export: bool,
19    columns: Vec<Column>,
20    keys: Vec<Key>,
21    indexes: Vec<Key>,
22    relations: Vec<Relation>,
23    reverse_relations: Vec<Relation>,
24    triggers: Vec<Trigger>,
25    constraints: Vec<Constraint>,
26    initial_data: Vec<InitialData>,
27    options: Vec<TableOption>,
28    aggregations: Vec<Aggregation>,
29}
30
31impl Table {
32    #[allow(clippy::too_many_arguments)]
33    pub fn new<S: Into<String>>(
34        schema_name: Option<S>,
35        name: S,
36        export_date_column: Option<S>,
37        lock_escalation: LockEscalation,
38        no_export: bool,
39        columns: Vec<Column>,
40        keys: Vec<Key>,
41        indexes: Vec<Key>,
42        relations: Vec<Relation>,
43        triggers: Vec<Trigger>,
44        constraints: Vec<Constraint>,
45        initial_data: Vec<InitialData>,
46        options: Vec<TableOption>,
47        aggregations: Vec<Aggregation>,
48    ) -> Self {
49        Self {
50            schema_name: schema_name.map(|s| s.into()),
51            name: name.into(),
52            export_date_column: export_date_column.map(|s| s.into()),
53            lock_escalation,
54            no_export,
55            columns,
56            keys,
57            indexes,
58            relations,
59            reverse_relations: Vec::new(),
60            triggers,
61            constraints,
62            initial_data,
63            options,
64            aggregations,
65        }
66    }
67
68    pub fn schema_name(&self) -> Option<&str> {
69        self.schema_name.as_deref()
70    }
71
72    pub fn name(&self) -> &str {
73        &self.name
74    }
75
76    pub fn export_date_column(&self) -> Option<&str> {
77        self.export_date_column.as_deref()
78    }
79
80    pub fn lock_escalation(&self) -> LockEscalation {
81        self.lock_escalation
82    }
83
84    pub fn is_no_export(&self) -> bool {
85        self.no_export
86    }
87
88    pub fn columns(&self) -> &[Column] {
89        &self.columns
90    }
91
92    pub fn keys(&self) -> &[Key] {
93        &self.keys
94    }
95
96    pub fn indexes(&self) -> &[Key] {
97        &self.indexes
98    }
99
100    pub fn relations(&self) -> &[Relation] {
101        &self.relations
102    }
103
104    pub fn reverse_relations(&self) -> &[Relation] {
105        &self.reverse_relations
106    }
107
108    pub fn triggers(&self) -> &[Trigger] {
109        &self.triggers
110    }
111
112    pub fn constraints(&self) -> &[Constraint] {
113        &self.constraints
114    }
115
116    pub fn initial_data(&self) -> &[InitialData] {
117        &self.initial_data
118    }
119
120    pub fn options(&self) -> &[TableOption] {
121        &self.options
122    }
123
124    pub fn aggregations(&self) -> &[Aggregation] {
125        &self.aggregations
126    }
127
128    pub fn column(&self, column_name: &str) -> &Column {
129        let lower = column_name.to_lowercase();
130        self.columns
131            .iter()
132            .find(|c| c.name().eq_ignore_ascii_case(&lower))
133            .unwrap_or_else(|| {
134                panic!(
135                    "Unable to locate a column with the name '{}' in table '{}'",
136                    column_name, self.name
137                )
138            })
139    }
140
141    pub fn primary_key(&self) -> Option<&Key> {
142        self.keys.iter().find(|k| k.key_type() == KeyType::Primary)
143    }
144
145    pub fn has_column(&self, column_name: &str) -> bool {
146        let lower = column_name.to_lowercase();
147        self.columns
148            .iter()
149            .any(|c| c.name().eq_ignore_ascii_case(&lower))
150    }
151
152    pub fn identity_column(&self) -> Option<&Column> {
153        self.columns.iter().find(|c| {
154            let t = c.column_type();
155            matches!(t, ColumnType::Sequence | ColumnType::LongSequence)
156        })
157    }
158
159    pub fn primary_key_columns(&self) -> Option<Vec<String>> {
160        self.primary_key()
161            .map(|k| k.columns().iter().map(|kc| kc.name().to_string()).collect())
162    }
163
164    pub fn has_option(&self, option: TableOption) -> bool {
165        self.options.iter().any(|o| *o == option)
166    }
167
168    pub fn has_column_constraints(&self, boolean_mode: BooleanMode) -> bool {
169        self.columns
170            .iter()
171            .any(|c| c.needs_check_constraints(boolean_mode))
172    }
173
174    pub fn columns_with_check_constraints(&self, boolean_mode: BooleanMode) -> Vec<Column> {
175        self.columns
176            .iter()
177            .filter(|c| c.needs_check_constraints(boolean_mode))
178            .cloned()
179            .collect()
180    }
181
182    pub fn column_relation(&self, column: &Column) -> Option<&Relation> {
183        let name = column.name();
184        self.relations
185            .iter()
186            .find(|r| r.from_column_name().eq_ignore_ascii_case(name))
187    }
188
189    pub fn fully_qualified_table_name(&self) -> String {
190        match self.schema_name() {
191            Some(schema_name) => format!("{}.{}", schema_name, self.name()),
192            None => self.name().to_string(),
193        }
194    }
195
196    pub fn add_reverse_relation(&mut self, relation: Relation) {
197        self.reverse_relations.push(relation);
198    }
199}
200
201impl fmt::Display for Table {
202    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203        match &self.schema_name {
204            Some(schema) => write!(f, "{}.{}", schema, self.name),
205            None => write!(f, "{}", self.name),
206        }
207    }
208}
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213    use crate::model::types::LockEscalation;
214
215    fn sample_table() -> Table {
216        Table::new(
217            Some("schema"),
218            "table",
219            Option::<&str>::None,
220            LockEscalation::Auto,
221            false,
222            Vec::new(),
223            Vec::new(),
224            Vec::new(),
225            Vec::new(),
226            Vec::new(),
227            Vec::new(),
228            Vec::new(),
229            Vec::new(),
230            Vec::new(),
231        )
232    }
233
234    #[test]
235    fn constructor_and_basic_getters() {
236        let t = sample_table();
237        assert_eq!(t.schema_name().unwrap(), "schema");
238        assert_eq!(t.name(), "table");
239        assert_eq!(t.export_date_column(), None);
240        assert_eq!(t.lock_escalation(), LockEscalation::Auto);
241        assert!(!t.is_no_export());
242        assert_eq!(format!("{}", t), "schema.table");
243    }
244}