use table::Table;
use writer::Writer;
use database::DatabaseDev;
use std::fs::File;
use std::fs;
use std::io::Write;
pub struct Config{
pub base_module:Option<String>,
pub include_table_references:bool,
pub use_condensed_name:bool,
pub generate_table_meta:bool,
pub base_dir: String,
}
impl Config{
pub fn default()->Self{
Config{
base_module:Some("gen".to_string()),
include_table_references:true,
use_condensed_name:true,
generate_table_meta:true,
base_dir:"./src".to_string(),
}
}
fn table_module(&self, table:&Table)->String{
let parent = self.module(&table.schema);
format!("{}::{}",parent,table.struct_name())
}
fn module_dir(&self, schema:&str)->String{
let base_module = self.base_module.clone();
match base_module{
Some(x) => format!("{}/{}/{}",self.base_dir, x, schema),
None => format!("{}/{}",self.base_dir, schema)
}
}
fn module_base_dir(&self)->String{
let base_module = self.base_module.clone();
match base_module{
Some(x) => format!("{}/{}",self.base_dir, x),
None => format!("./{}",self.base_dir)
}
}
fn module(&self, schema:&str)->String{
let base_module = self.base_module.clone();
match base_module{
Some(x) => format!("{}::{}", x, schema),
None => format!("{}", schema)
}
}
}
pub fn get_all_tables<T:DatabaseDev>(db_dev:&T)->Vec<Table>{
let all_tables_names = db_dev.get_all_tables();
let mut all_table_def:Vec<Table> = Vec::new();
for (schema, table) in all_tables_names{
println!("Extracted {}.{}", schema,table);
let meta = db_dev.get_table_metadata(&schema, &table);
all_table_def.push(meta);
}
all_table_def
}
pub fn get_schemas(all_table:&Vec<Table>)->Vec<String>{
let mut schema_names = Vec::new();
for t in all_table{
if !schema_names.contains(&t.schema){
schema_names.push(t.schema.clone());
}
}
schema_names.sort_by(|a, b| a.cmp(b));
schema_names
}
pub fn get_tables_in_schema<'a>(schema:&str, all_table:&'a Vec<Table>)->Vec<&'a Table>{
let mut tables = Vec::new();
for t in all_table{
if &t.schema == schema{
tables.push(t); }
}
tables.sort_by(|a, b| a.name.cmp(&b.name));
tables
}
pub fn generate_all<T:DatabaseDev>(db_dev:&T, config:&Config){
let all_tables:Vec<Table> = get_all_tables(db_dev);
for table in &all_tables{
generate_table(db_dev, config, table, &all_tables);
}
generate_mod_per_schema(&config, &all_tables);
generate_mod_rs(&config, &all_tables);
}
fn generate_table<T:DatabaseDev>(db_dev:&T, config:&Config, table:&Table, all_tables:&Vec<Table>){
let mut w = Writer::new();
let (struct_imports, imported_tables, struct_src) = db_dev.to_struct_source_code(&table, all_tables);
let (dao_imports, dao_src) = generate_dao_conversion_code(table, all_tables);
let (meta_imports, meta_src) = generate_meta_code(table);
let static_columns = generate_static_column_names(table);
let warning = format!(" WARNING: This file is generated, derived from table {}, DO NOT EDIT", table.complete_name());
w.inner_doc_comment(&warning);
w.ln();
w.ln();
for i in struct_imports{
w.appendln(&format!("use {};",i));
}
for it in imported_tables{
if it != table{
w.appendln(&format!("use {};", config.table_module(it)));
}
}
for i in dao_imports{
w.appendln(&format!("use {};",i));
}
for i in meta_imports{
w.appendln(&format!("use {};",i));
}
w.ln();
w.ln();
w.appendln(&struct_src);
w.ln();
w.appendln(&static_columns);
w.ln();
w.appendln(&dao_src);
w.ln();
w.append(&meta_src);
let module_dir = config.module_dir(&table.schema);
fs::create_dir_all(&module_dir);
let file = format!("{}/{}.rs", &module_dir, table.name);
save_to_file(&file, &w.src);
}
fn generate_mod_per_schema(config:&Config, all_tables:&Vec<Table>){
let schemas = get_schemas(all_tables);
for schema in schemas{
let mut w = Writer::new();
let module_dir = config.module_dir(&schema);
let tables = get_tables_in_schema(&schema, all_tables);
for table in &tables{
w.append(&format!("pub mod {};",table.name));
w.ln();
}
for table in &tables{
w.append(&format!("pub use self::{}::{};", table.name, table.struct_name()));
w.ln();
}
let mod_file = format!("{}/mod.rs", module_dir);
save_to_file(&mod_file, &w.src);
}
}
fn generate_mod_rs(config:&Config, all_tables:&Vec<Table>){
let mut mod_src = Writer::new();
let schemas = get_schemas(&all_tables);
for schema in &schemas{
mod_src.append("pub mod ");
mod_src.append(schema);
mod_src.append(";");
mod_src.ln();
}
mod_src.append("use rustorm::table::Table;");
mod_src.ln();
mod_src.append("use rustorm::table::IsTable;");
mod_src.ln();
mod_src.ln();
for table in all_tables{
let table_mod = format!("use {};", config.table_module(table));
mod_src.append(&table_mod);
mod_src.ln();
}
let mod_file = format!("{}/mod.rs",config.module_base_dir());
let all_table_fn = &generate_fn_get_all_tables(&all_tables);
mod_src.append(all_table_fn);
save_to_file(&mod_file, &mod_src.src);
}
fn generate_meta_code(table: &Table)->(Vec<String>, String){
let mut w = Writer::new();
let mut imports = Vec::new();
imports.push("rustorm::table::IsTable".to_string());
imports.push("rustorm::table::Column".to_string());
imports.push("rustorm::table::Table".to_string());
w.append("impl IsTable for ");
w.append(&table.struct_name());
w.append("{");
w.ln();
w.ln();
w.tab();
w.append("fn table()->Table{");
w.ln();
w.tab();
let (table_imports, table_src) = table.to_tabledef_source_code();
for imp in table_imports{
imports.push(imp);
}
w.append(&table_src);
w.ln();
w.tab();
w.append("}");
w.ln();
w.append("}");
(imports, w.src)
}
fn generate_static_column_names(table: &Table)->String{
let mut w = Writer::new();
w.comment(" Generated columns for easier development of dynamic queries without sacrificing wrong spelling of column names");
for column in &table.columns{
w.ln();
w.ln();
w.appendln("#[allow(non_upper_case_globals)]");
w.appendln("#[allow(dead_code)]");
w.append("pub static ");
w.append(&column.name);
w.append(": &'static str = ");
w.append(&format!("\"{}.{}\"", table.name, column.name));
w.append(";");
}
w.src
}
fn generate_dao_conversion_code(table: &Table, all_tables:&Vec<Table>)->(Vec<String>, String){
let mut w = Writer::new();
let mut imports = Vec::new();
imports.push("rustorm::dao::Dao".to_string());
imports.push("rustorm::dao::IsDao".to_string());
w.ln();
w.append("impl IsDao for ");
w.append(&table.struct_name());
w.append("{");
w.ln_tab();
w.append("fn from_dao(dao:&Dao)->Self{");
w.ln_tabs(2);
w.append(&table.struct_name());
w.append("{");
for c in &table.columns{
w.ln_tabs(3);
w.append(&c.name);
w.append(": ");
if c.not_null{
w.append("dao.get");
}else{
w.append("dao.get_opt");
}
w.append("(\"");
w.append(&c.name);
w.append("\")");
w.comma();
}
let referenced_tables = table.get_all_referenced_table(all_tables);
for ref_table in referenced_tables{
let member_name = ref_table.member_name(table);
w.ln_tabs(3);
w.append(&member_name);
w.append(": ");
if ref_table.is_has_one{
w.append("None");
}
if ref_table.is_ext{
w.append("None");
}
if ref_table.is_has_many{
w.append("vec![]");
}
w.comma();
}
w.ln_tabs(2);
w.append("}");
w.ln_tab();
w.append("}");
w.ln();
w.append("}");
(imports, w.src)
}
fn generate_fn_get_all_tables(tables:&Vec<Table>)->String{
let mut w = Writer::new();
w.ln();
w.ln();
w.append("pub fn get_all_tables()->Vec<Table>{");
w.ln();
w.tab();
w.append("vec![");
for t in tables{
w.ln();
w.tabs(2);
w.append(&t.struct_name());
w.append("::table(),");
}
w.ln();
w.tab();
w.append("]");
w.ln();
w.append("}");
w.src
}
fn save_to_file(filename: &str, content:&str){
match File::create(filename){
Err(_) => panic!("couldn't create file {}", filename),
Ok(mut file) => {
match file.write_all(content.as_bytes()){
Ok(_) => {println!("Saved to {}",filename);},
Err(_) => {println!("There was error saving to file: {}",filename)}
};
},
};
}