use clap::Parser;
use tern_core::error::TernResult;
use tern_core::future::Future;
use tern_core::migration::MigrationContext;
use tern_core::runner::{Report, Runner};
mod cli;
mod commands;
pub trait ContextOptions {
type Ctx: MigrationContext;
fn connect(
&self,
db_url: &str,
) -> impl Future<Output = TernResult<Self::Ctx>>;
}
pub struct App<T> {
inner: T,
cli: cli::Tern,
}
impl<T> App<T> {
pub fn new(inner: T) -> Self {
let cli = cli::Tern::parse();
Self { inner, cli }
}
pub async fn run(&self) -> anyhow::Result<Option<Report>>
where
T: ContextOptions,
{
match &self.cli.commands {
cli::TernCommands::History(history) => match &history.commands {
cli::HistoryCommands::Init { connect_opts } => {
let db_url = connect_opts.required_db_url()?.to_string();
let context = self.inner.connect(&db_url).await?;
let mut runner = Runner::new(context);
runner.init_history().await?;
Ok(None)
},
cli::HistoryCommands::Drop { connect_opts } => {
let db_url = connect_opts.required_db_url()?.to_string();
let context = self.inner.connect(&db_url).await?;
let mut runner = Runner::new(context);
runner.drop_history().await?;
Ok(None)
},
cli::HistoryCommands::SoftApply { .. } => Err(anyhow::anyhow!(
"Deprecated: use `migrate soft-apply` instead"
)),
},
cli::TernCommands::Migrate(migrate) => match &migrate.commands {
cli::MigrateCommands::Apply {
dryrun,
target_version,
connect_opts,
} => {
let db_url = connect_opts.required_db_url()?.to_string();
let context = self.inner.connect(&db_url).await?;
let mut runner = Runner::new(context);
let report =
runner.run_apply(*target_version, *dryrun).await?;
Ok(Some(report))
},
cli::MigrateCommands::ApplyAll { dryrun, connect_opts } => {
let db_url = connect_opts.required_db_url()?.to_string();
let context = self.inner.connect(&db_url).await?;
let mut runner = Runner::new(context);
let report = runner.run_apply_all(*dryrun).await?;
Ok(Some(report))
},
cli::MigrateCommands::SoftApply {
dryrun,
target_version,
connect_opts,
} => {
let db_url = connect_opts.required_db_url()?.to_string();
let context = self.inner.connect(&db_url).await?;
let mut runner = Runner::new(context);
let report =
runner.run_soft_apply(*target_version, *dryrun).await?;
Ok(Some(report))
},
cli::MigrateCommands::ListApplied { connect_opts } => {
let db_url = connect_opts.required_db_url()?.to_string();
let context = self.inner.connect(&db_url).await?;
let mut runner = Runner::new(context);
let report = runner.list_applied().await?;
Ok(Some(report))
},
cli::MigrateCommands::New {
description,
no_tx,
migration_type,
source,
} => {
commands::new(
description.to_string(),
*no_tx,
*migration_type,
source.path.clone(),
)?;
Ok(None)
},
},
}
}
pub async fn run_with_context(self) -> anyhow::Result<Option<Report>>
where
T: MigrationContext,
{
let mut runner = Runner::new(self.inner);
let cli = self.cli;
match cli.commands {
cli::TernCommands::History(history) => match &history.commands {
cli::HistoryCommands::Init { .. } => {
runner.init_history().await?;
Ok(None)
},
cli::HistoryCommands::Drop { .. } => {
runner.drop_history().await?;
Ok(None)
},
cli::HistoryCommands::SoftApply { .. } => Err(anyhow::anyhow!(
"Deprecated: use `migrate soft-apply` instead"
)),
},
cli::TernCommands::Migrate(migrate) => match migrate.commands {
cli::MigrateCommands::Apply {
dryrun, target_version, ..
} => {
let report =
runner.run_apply(target_version, dryrun).await?;
Ok(Some(report))
},
cli::MigrateCommands::ApplyAll { dryrun, .. } => {
let report = runner.run_apply_all(dryrun).await?;
Ok(Some(report))
},
cli::MigrateCommands::SoftApply {
dryrun,
target_version,
..
} => {
let report =
runner.run_soft_apply(target_version, dryrun).await?;
Ok(Some(report))
},
cli::MigrateCommands::ListApplied { .. } => {
let report = runner.list_applied().await?;
Ok(Some(report))
},
cli::MigrateCommands::New {
description,
no_tx,
migration_type,
source,
} => {
commands::new(
description.to_string(),
no_tx,
migration_type,
source.path.clone(),
)?;
Ok(None)
},
},
}
}
}