153 lines
5.1 KiB
Rust
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(())
|
|
}
|