use std::{ffi::OsStr, path::Path}; use attribute_derive::FromAttr; use argh::FromArgs; use anyhow::{Result, anyhow}; use crate::generators::{SourceNode, SourceNodeContainer}; // use gen_migrations::generate_create_table_sql; // use gen_repositories::{generate_repositories_source_files, SourceNodeContainer}; pub mod models; pub mod parse_models; pub mod generators; #[derive(FromAttr, PartialEq, Debug, Default)] #[attribute(ident = sql_generator_model)] pub struct SqlGeneratorModelAttr { table_name: Option } #[derive(FromAttr, PartialEq, Debug, Default)] #[attribute(ident = sql_generator_field)] pub struct SqlGeneratorFieldAttr { is_primary: Option, is_unique: Option, reverse_relation_name: Option, /// to indicate that this field will be used to obtains entities /// our framework will generate methods for all fields that is an entrypoint is_query_entrypoint: Option } #[derive(FromArgs, PartialEq, Debug)] /// Generate SQL CREATE TABLE migrations #[argh(subcommand, name = "gen-migrations")] struct GenerateMigration { /// path of file where to write all in one generated SQL migration #[argh(option, short = 'o')] output: Option } #[derive(FromArgs, PartialEq, Debug)] /// Generate Rust SQLx repositories code #[argh(subcommand, name = "gen-repositories")] struct GenerateRepositories { /// path of the directory that contains repositories #[argh(option, short = 'o')] output: Option } #[derive(FromArgs, PartialEq, Debug)] #[argh(subcommand)] enum GeneratorArgsSubCommands { GenerateMigration(GenerateMigration), GenerateRepositories(GenerateRepositories), } #[derive(FromArgs)] /// SQLX Generator args struct GeneratorArgs { /// path where to find Cargo.toml #[argh(option)] project_root: Option, /// path of the directory containing models #[argh(option, short = 'm')] models_path: Option, #[argh(subcommand)] nested: GeneratorArgsSubCommands } fn write_source_code(base_path: &Path, snc: SourceNodeContainer) -> Result<()> { let path = base_path.join(snc.name); match snc.inner { SourceNode::File(code) => { println!("writing file {:?}", path); std::fs::write(path, code)?; }, SourceNode::Directory(dir) => { for node in dir { write_source_code(&path, node)?; } } } Ok(()) } pub fn main() -> Result<()> { let args: GeneratorArgs = argh::from_env(); let project_root = &args.project_root.unwrap_or(".".to_string()); let project_root_path = Path::new(&project_root); eprintln!("Using project root at: {:?}", &project_root_path.canonicalize()?); if !project_root_path.exists() { return Err(anyhow!("Could not resolve project root path.")); } // check Cargo.toml let main_manifest_location = "Cargo.toml"; let main_manifest_path = project_root_path.join(main_manifest_location); if !main_manifest_path.exists() { return Err(anyhow!("Could not find Cargo.toml in project root.")); } // search for a models modules let models_mod_location = "src/models.rs"; let mut models_mod_path = project_root_path.join(models_mod_location); if let Some(models_path) = args.models_path { models_mod_path = project_root_path.join(models_path); } if !models_mod_path.exists() { let models_mod_location = "src/models/mod.rs"; models_mod_path = project_root_path.join(models_mod_location); } if !models_mod_path.exists() { return Err(anyhow!("Could not resolve models modules.")); } if models_mod_path.file_name().map(|x| x == OsStr::new("mod.rs")).unwrap_or(false) { models_mod_path.pop(); } eprintln!("Found models in project, parsing models"); let models = parse_models::parse_models_from_module(&models_mod_path)?; dbg!(&models); match args.nested { GeneratorArgsSubCommands::GenerateRepositories(opts) => { eprintln!("Generating repositories…"); // search for a repository module let repositories_mod_location = opts.output.unwrap_or("src/repositories".to_string()); let repositories_mod_path = project_root_path.join(repositories_mod_location); if !repositories_mod_path.exists() { return Err(anyhow!("Could not resolve repositories modules.")); } let snc = generators::repositories::generate_repositories_source_files(&models)?; dbg!(&snc); write_source_code(&repositories_mod_path, snc)?; }, GeneratorArgsSubCommands::GenerateMigration(opts) => { eprintln!("Generating migrations…"); let sql_code = generators::migrations::generate_create_table_sql(&models)?; if let Some(out_location) = opts.output { let output_path = Path::new(&out_location); let write_res = std::fs::write(output_path, sql_code); eprintln!("{:?}", write_res); } else { println!("{}", sql_code); } } } Ok(()) }