sqlxgentools/lib/sqlxgentools_cli/src/main.rs
2026-01-05 14:07:27 +01:00

153 lines
5.1 KiB
Rust

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<String>
}
#[derive(FromAttr, PartialEq, Debug, Default)]
#[attribute(ident = sql_generator_field)]
pub struct SqlGeneratorFieldAttr {
is_primary: Option<bool>,
is_unique: Option<bool>,
reverse_relation_name: Option<String>,
/// 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<bool>
}
#[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<String>
}
#[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<String>
}
#[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<String>,
/// path of the directory containing models
#[argh(option, short = 'm')]
models_path: Option<String>,
#[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(())
}