use clap::Parser; use dotenvy::dotenv; use std::{error::Error, fmt::Display, process::exit}; use tracing_subscriber::{prelude::*, EnvFilter}; use sea_orm::{ConnectOptions, Database, DbConn}; use sea_orm_cli::{run_migrate_generate, run_migrate_init, MigrateSubcommands}; use super::MigratorTrait; const MIGRATION_DIR: &str = "./"; pub async fn run_cli(migrator: M) where M: MigratorTrait, { dotenv().ok(); let cli = Cli::parse(); let url = cli .database_url .expect("Environment variable 'DATABASE_URL' not set"); let schema = cli.database_schema.unwrap_or_else(|| "public".to_owned()); let connect_options = ConnectOptions::new(url) .set_schema_search_path(schema) .to_owned(); let db = &Database::connect(connect_options) .await .expect("Fail to acquire database connection"); run_migrate(migrator, db, cli.command, cli.verbose) .await .unwrap_or_else(handle_error); } pub async fn run_migrate( _: M, db: &DbConn, command: Option, verbose: bool, ) -> Result<(), Box> where M: MigratorTrait, { let filter = match verbose { true => "debug", false => "sea_orm_migration=info", }; let filter_layer = EnvFilter::try_new(filter).unwrap(); if verbose { let fmt_layer = tracing_subscriber::fmt::layer(); tracing_subscriber::registry() .with(filter_layer) .with(fmt_layer) .init() } else { let fmt_layer = tracing_subscriber::fmt::layer() .with_target(false) .with_level(false) .without_time(); tracing_subscriber::registry() .with(filter_layer) .with(fmt_layer) .init() }; match command { Some(MigrateSubcommands::Fresh) => M::fresh(db).await?, Some(MigrateSubcommands::Refresh) => M::refresh(db).await?, Some(MigrateSubcommands::Reset) => M::reset(db).await?, Some(MigrateSubcommands::Status) => M::status(db).await?, Some(MigrateSubcommands::Up { num }) => M::up(db, num).await?, Some(MigrateSubcommands::Down { num }) => M::down(db, Some(num)).await?, Some(MigrateSubcommands::Init) => run_migrate_init(MIGRATION_DIR)?, Some(MigrateSubcommands::Generate { migration_name, universal_time, }) => run_migrate_generate(MIGRATION_DIR, &migration_name, universal_time)?, _ => M::up(db, None).await?, }; Ok(()) } #[derive(Parser)] #[clap(version)] pub struct Cli { #[clap(action, short = 'v', long, global = true, help = "Show debug messages")] verbose: bool, #[clap( value_parser, global = true, short = 's', long, env = "DATABASE_SCHEMA", long_help = "Database schema\n \ - For MySQL and SQLite, this argument is ignored.\n \ - For PostgreSQL, this argument is optional with default value 'public'.\n" )] database_schema: Option, #[clap( value_parser, global = true, short = 'u', long, env = "DATABASE_URL", help = "Database URL" )] database_url: Option, #[clap(subcommand)] command: Option, } fn handle_error(error: E) where E: Display, { eprintln!("{}", error); exit(1); }