use anyhow::Result;
use clap::{Parser, Subcommand};
use std::path::PathBuf;
use tracing::info;
use tracing_subscriber::EnvFilter;
use rdbi_codegen::config::CodegenConfig;
#[derive(Parser)]
#[command(name = "rdbi-codegen")]
#[command(about = "Generate Rust structs and rdbi DAO functions from MySQL schema DDL")]
#[command(version)]
struct Cli {
#[arg(short, long)]
config: Option<PathBuf>,
#[arg(short, long)]
schema: Option<PathBuf>,
#[arg(short, long)]
output: Option<PathBuf>,
#[arg(long)]
dry_run: bool,
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand)]
enum Commands {
Generate,
Structs,
Dao,
Inspect,
}
fn main() -> Result<()> {
let cli = Cli::parse();
let mut config = if let Some(config_path) = &cli.config {
CodegenConfig::from_file(config_path)?
} else {
CodegenConfig::default()
};
let default_level = if cfg!(debug_assertions) {
"debug"
} else {
"info"
};
let log_level = config.log_level.as_deref().unwrap_or(default_level);
tracing_subscriber::fmt()
.with_env_filter(
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(log_level)),
)
.init();
if let Some(schema) = cli.schema {
config.schema_file = schema;
}
if let Some(output) = cli.output {
config.output_structs_dir = output.join("models");
config.output_dao_dir = output.join("dao");
}
if cli.dry_run {
config.dry_run = true;
}
match &cli.command {
Some(Commands::Structs) => {
config.generate_dao = false;
}
Some(Commands::Dao) => {
config.generate_structs = false;
}
Some(Commands::Inspect) => {
return inspect_schema(&config);
}
_ => {}
}
config.validate()?;
info!("Generating code from schema: {:?}", config.schema_file);
if config.dry_run {
println!("Dry run mode - would generate:");
let schema_sql = std::fs::read_to_string(&config.schema_file)?;
let tables = rdbi_codegen::parser::parse_schema(&schema_sql)?;
for table in &tables {
if config.generate_structs {
println!(
" Struct: {}/{}.rs",
config.output_structs_dir.display(),
table.name
);
}
if config.generate_dao {
println!(
" DAO: {}/{}.rs",
config.output_dao_dir.display(),
table.name
);
}
}
return Ok(());
}
rdbi_codegen::generate(&config)?;
info!("Code generation completed successfully");
Ok(())
}
fn inspect_schema(config: &CodegenConfig) -> Result<()> {
let schema_sql = std::fs::read_to_string(&config.schema_file)?;
let tables = rdbi_codegen::parser::parse_schema(&schema_sql)?;
println!("Parsed {} tables:\n", tables.len());
for table in &tables {
println!("Table: {}", table.name);
println!(" Columns:");
for col in &table.columns {
let nullable = if col.nullable { "NULL" } else { "NOT NULL" };
let auto_inc = if col.is_auto_increment {
" AUTO_INCREMENT"
} else {
""
};
println!(
" - {} {} {}{}",
col.name, col.data_type, nullable, auto_inc
);
if let Some(enum_values) = &col.enum_values {
println!(" ENUM values: {:?}", enum_values);
}
}
if let Some(pk) = &table.primary_key {
println!(" Primary Key: {:?}", pk.columns);
}
if !table.indexes.is_empty() {
println!(" Indexes:");
for idx in &table.indexes {
let unique = if idx.unique { "UNIQUE " } else { "" };
println!(" - {}INDEX {} ({:?})", unique, idx.name, idx.columns);
}
}
if !table.foreign_keys.is_empty() {
println!(" Foreign Keys:");
for fk in &table.foreign_keys {
println!(
" - {} -> {}.{}",
fk.column_name, fk.referenced_table, fk.referenced_column
);
}
}
println!();
}
Ok(())
}