use clap::{Arg, Command};
use schema_model::model::database_model::DatabaseModel;
use schema_model::model::types::{BooleanMode, ForeignKeyMode};
use schema_parser::parse_database_xml;
use schema_sql_generator::common::generate_options::GenerateOptions;
use schema_sql_generator::common::generator_type::GeneratorType;
use schema_sql_generator::common::output_mode::OutputMode;
use schema_sql_generator::common::print_writer::PrintWriter;
use std::cell::RefCell;
use std::fs::File;
use std::path::Path;
use std::rc::Rc;
use std::{env, fs};
pub fn main() {
let args: Vec<String> = env::args().collect();
println!("Program arguments: {:?}", args);
let arguments = Command::new("schema")
.version("1.0")
.author("Jeff Stano <jeff@stano.com>")
.about("Manages database schemas")
.arg(Arg::new("database-type")
.long("database-type")
.value_name("TYPE")
.value_parser(["postgres", "sqlite", "sqlserver"])
.required(true)
.num_args(1)
.ignore_case(true)
.help("Sets the database type"))
.arg(Arg::new("schema-file")
.long("schema-file")
.value_name("FILE")
.required(true)
.help("Sets the schema file location"))
.arg(Arg::new("foreign-key-mode")
.long("foreign-key-mode")
.value_name("MODE")
.value_parser(["none", "relations", "triggers"])
.help("Sets the foreign key mode"))
.arg(Arg::new("boolean-mode")
.long("boolean-mode")
.value_name("MODE")
.value_parser(["native", "yesno", "yn"])
.help("Sets the boolean mode"))
.arg(Arg::new("output-mode")
.long("output-mode")
.value_name("MODE")
.value_parser(["all", "indexes-only", "triggers-only"])
.help("Sets the output mode"))
.arg(Arg::new("pg-version")
.long("pg-version")
.value_name("VERSION")
.help("Target PostgreSQL major version (e.g. 17, 18); affects UUID default function"))
.get_matches();
let empty = String::new();
let database_type = arguments.get_one::<String>("database-type").expect("required argument --database-type missing");
let schema_file = arguments.get_one::<String>("schema-file").expect("required argument --schema-file missing");
let foreign_key_mode = arguments.get_one::<String>("foreign-key-mode").unwrap_or(&empty);
let boolean_mode = arguments.get_one::<String>("boolean-mode").unwrap_or(&empty);
let output_mode = arguments.get_one::<String>("output-mode").unwrap_or(&empty);
let target_postgres_version: u32 = arguments
.get_one::<String>("pg-version")
.and_then(|v| v.parse().ok())
.unwrap_or(0);
let schema_path = Path::new(schema_file);
let output_path = build_output_path(schema_path, database_type.to_string().to_lowercase());
let output_file = File::create(output_path).expect("");
let print_writer = PrintWriter::new(Box::new(output_file));
let generator_type: GeneratorType = database_type.parse().unwrap();
let database_model = load_schema(schema_path);
let options = GenerateOptions {
database_model: Rc::new(database_model),
writer: Rc::new(RefCell::new(print_writer)),
boolean_mode: boolean_mode.parse().unwrap_or(BooleanMode::Native),
foreign_key_mode: foreign_key_mode.parse().unwrap_or(ForeignKeyMode::Relations),
output_mode: output_mode.parse().unwrap_or(OutputMode::All),
target_postgres_version,
};
generator_type.generate(options);
}
fn load_schema(schema_path: &Path) -> DatabaseModel {
let contents = fs::read_to_string(schema_path).expect("failed to read the schema file");
parse_database_xml(contents.as_str()).expect("failed to parse the schema")
}
fn build_output_path(path: &Path, database_type: String) -> String {
let parent = path.parent().expect("Path has no parent");
let stem = path.file_stem().expect("No file stem").to_string_lossy();
let new_filename = format!("{}-{}.sql", stem, database_type);
parent.join(new_filename).to_string_lossy().to_string()
}