use clap::{CommandFactory, Parser, Subcommand};
use clap_complete::{generate, Shell};
use dsync::{error::IOErrorToError, GenerationConfig, TableOptions};
use std::collections::HashMap;
use std::io::{BufWriter, Write};
use std::path::PathBuf;
#[derive(Debug, Parser, Clone, PartialEq)]
#[command(author, version, about, long_about = None)]
#[command(bin_name("dsync"))]
#[command(disable_help_subcommand(true))] #[command(subcommand_negates_reqs(true))]
#[command(infer_subcommands(true))]
pub struct CliDerive {
#[clap(flatten)]
pub args: Option<MainOptions>,
#[command(subcommand)]
pub subcommands: Option<SubCommands>,
}
#[derive(Debug, Subcommand, Clone, PartialEq)]
pub enum SubCommands {
Completions(CommandCompletions),
}
#[derive(Debug, Parser, Clone, PartialEq)]
pub struct CommandCompletions {
#[arg(short = 's', long = "shell", value_enum)]
pub shell: Shell,
#[arg(short = 'o', long = "out")]
pub output_file_path: Option<PathBuf>,
}
#[derive(Debug, Parser, Clone, PartialEq)]
pub struct MainOptions {
#[arg(short = 'i', long = "input")]
pub input: PathBuf,
#[arg(short = 'o', long = "output")]
pub output: PathBuf,
#[arg(long = "tsync")]
#[cfg(feature = "tsync")]
pub tsync: bool,
#[arg(long = "async")]
#[cfg(feature = "async")]
pub use_async: bool,
#[arg(short = 'g', long = "autogenerated-columns")]
pub autogenerated_columns: Option<Vec<String>>,
#[arg(short = 'c', long = "connection-type")]
pub connection_type: String,
#[arg(long = "no-serde")]
pub no_serde: bool,
#[arg(long = "schema-path", default_value = "crate::schema::")]
pub schema_path: String,
#[arg(long = "model-path", default_value = "crate::models::")]
pub model_path: String,
#[arg(long = "no-crud")]
pub no_crud: bool,
}
fn main() {
let res = actual_main();
if let Err(err) = res {
eprintln!("Error:\n{err}");
#[cfg(feature = "backtrace")]
{
let backtrace = err.backtrace().to_string();
if backtrace == "disabled backtrace" {
eprintln!(
"note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace"
);
} else {
eprintln!("{}", backtrace);
}
}
#[cfg(not(feature = "backtrace"))]
{
eprintln!("backtrace support is disabled, enable feature \"backtrace\"");
}
std::process::exit(1);
}
}
fn actual_main() -> dsync::Result<()> {
let cli = CliDerive::parse();
if let Some(subcommand) = cli.subcommands {
return match subcommand {
SubCommands::Completions(subcommand) => command_completions(&subcommand),
};
}
let args = cli
.args
.expect("cli.args should be defined if no subcommand is given");
let cols = args.autogenerated_columns.unwrap_or_default();
let mut default_table_options = TableOptions::default()
.autogenerated_columns(cols.iter().map(|t| t.as_str()).collect::<Vec<&str>>());
#[cfg(feature = "tsync")]
if args.tsync {
default_table_options = default_table_options.tsync();
}
#[cfg(feature = "async")]
if args.use_async {
default_table_options = default_table_options.use_async();
}
if args.no_serde {
default_table_options = default_table_options.disable_serde();
}
if args.no_crud {
default_table_options = default_table_options.disable_fns();
}
dsync::generate_files(
args.input,
args.output,
GenerationConfig {
default_table_options,
table_options: HashMap::from([]),
connection_type: args.connection_type,
schema_path: args.schema_path,
model_path: args.model_path,
},
)?;
Ok(())
}
#[inline]
pub fn command_completions(sub_args: &CommandCompletions) -> dsync::Result<()> {
let mut writer: BufWriter<Box<dyn Write>> = match &sub_args.output_file_path {
Some(v) => {
if v.exists() {
return Err(dsync::Error::other("Output file already exists"));
}
let v_parent = v
.parent()
.expect("Expected input filename to have a parent");
std::fs::create_dir_all(v_parent).attach_path_err(v_parent)?;
BufWriter::new(Box::from(std::fs::File::create(v).attach_path_err(v)?))
}
None => BufWriter::new(Box::from(std::io::stdout())),
};
let mut parsed = CliDerive::command();
let bin_name = parsed
.get_bin_name()
.expect("Expected binary to have a binary name")
.to_string();
generate(sub_args.shell, &mut parsed, bin_name, &mut writer);
Ok(())
}