1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
use rustorm::table::ForeignKey;
use rustorm::ColumnName;
use rustorm::TableName;

use data_container::DropdownInfo;
use data_container::IdentifierDisplay;
use field::Field;
use rustorm::Column;
use rustorm::Table;
use table_intel;

#[derive(Debug, Serialize, Clone)]
pub struct Tab {
    pub name: String,
    pub description: Option<String>,
    pub table_name: TableName,
    /// simple fields, the lookup fields are not included
    /// in these
    pub fields: Vec<Field>,
    pub is_view: bool,
    pub display: Option<IdentifierDisplay>,
}

impl Tab {
    pub fn from_table(table: &Table, name: Option<String>, tables: &Vec<Table>) -> Self {
        let fields = Self::derive_fields(table, tables);
        let display = Self::derive_display(table);
        let tab_name = match name {
            Some(name) => name,
            None => table.name.name.to_string(),
        };
        Tab {
            name: tab_name,
            description: table.comment.to_owned(),
            table_name: table.name.to_owned(),
            fields: fields,
            is_view: table.is_view,
            display,
        }
    }

    fn derive_fields(table: &Table, tables: &Vec<Table>) -> Vec<Field> {
        let mut fields = Vec::with_capacity(table.columns.len());
        fields.extend(Self::derive_simple_fields(table));
        fields.extend(Self::derive_foreign_fields(table, tables));
        fields
    }

    pub fn derive_dropdowninfo(table: &Table) -> Option<DropdownInfo> {
        match Self::derive_display(table) {
            Some(display) => Some(DropdownInfo {
                source: table.name.clone(),
                display,
            }),
            None => None,
        }
    }

    // TODO: also make a function to do derive_image_display to detect
    // images that are displayeable
    fn derive_display(table: &Table) -> Option<IdentifierDisplay> {
        let table_name = &table.name.name;
        let columns = &table.columns;
        let pk: Vec<ColumnName> = table
            .get_primary_column_names()
            .iter()
            .map(|ref column| (**column).to_owned())
            .collect();
        // match for users table common structure
        let display = if table_name == "user" || table_name == "users" {
            let found_column = columns.iter().find(|column| {
                let column_name = &column.name.name;
                *column_name == "username" || *column_name == "email"
            });
            found_column.map(|column| IdentifierDisplay {
                columns: vec![column.name.clone()],
                separator: None,
                pk: pk.clone(),
            })
        }
        // match the column name regardless of the table name
        else {
            let found_column = columns.iter().find(|column| {
                let column_name = &column.name.name;
                *column_name == "name" || *column_name == "title"
            });
            found_column.map(|column| IdentifierDisplay {
                columns: vec![column.name.clone()],
                separator: None,
                pk: pk.clone(),
            })
        };

        // match for person common columns
        display.or_else(|| {
            let maybe_firstname = columns.iter().find(|column| {
                let column_name = &column.name.name;
                *column_name == "first_name" || *column_name == "firstname"
            });

            let maybe_lastname = columns.iter().find(|column| {
                let column_name = &column.name.name;
                *column_name == "last_name" || *column_name == "lastname"
            });
            if let Some(lastname) = maybe_lastname {
                if let Some(firstname) = maybe_firstname {
                    Some(IdentifierDisplay {
                        columns: vec![lastname.name.clone(), firstname.name.clone()],
                        separator: Some(", ".into()),
                        pk: pk.clone(),
                    })
                } else {
                    None
                }
            } else {
                let same_name = columns.iter().find(|column| {
                    let column_name = &column.name.name;
                    column_name == table_name
                });

                match same_name {
                    Some(column) => Some(IdentifierDisplay {
                        columns: vec![column.name.clone()],
                        separator: None,
                        pk: pk.clone(),
                    }),
                    None => {
                        // empty display columns
                        Some(IdentifierDisplay {
                            columns: vec![],
                            separator: None,
                            pk: pk.clone(),
                        })
                    }
                }
            }
        })
    }

    fn derive_simple_fields(table: &Table) -> Vec<Field> {
        let columns: &Vec<Column> = &table.columns;
        let foreign_column_names: Vec<&ColumnName> = table.get_foreign_column_names();
        let plain_columns: Vec<&Column> = columns
            .iter()
            .filter(|c| !foreign_column_names.contains(&&c.name))
            .collect();
        let mut fields: Vec<Field> = Vec::with_capacity(plain_columns.len());
        for pc in plain_columns {
            let field = Field::from_column(table, pc);
            fields.push(field)
        }
        fields
    }

    fn derive_foreign_fields(table: &Table, all_tables: &Vec<Table>) -> Vec<Field> {
        let foreign_keys: Vec<&ForeignKey> = table.get_foreign_keys();
        let mut fields: Vec<Field> = Vec::with_capacity(foreign_keys.len());
        for fk in foreign_keys {
            let mut columns: Vec<&Column> = Vec::with_capacity(fk.columns.len());
            for fc in &fk.columns {
                if let Some(col) = table.get_column(fc) {
                    columns.push(col);
                }
            }
            let foreign_table = table_intel::get_table(&fk.foreign_table, all_tables);
            if let Some(foreign_table) = foreign_table {
                let field = Field::from_has_one_table(table, &columns, foreign_table);
                fields.push(field);
            }
        }
        fields
    }

    pub fn get_display_columns(&self) -> Vec<&ColumnName> {
        match *&self.display {
            Some(ref display) => display.columns.iter().map(|ref column| *column).collect(),
            None => vec![],
        }
    }

    pub fn has_column_name(&self, column_name: &ColumnName) -> bool {
        self.fields
            .iter()
            .any(|field| field.has_column_name(column_name))
    }
}

pub fn find_tab<'a>(tabs: &'a Vec<Tab>, table_name: &TableName) -> Option<&'a Tab> {
    tabs.iter().find(|tab| tab.table_name == *table_name)
}