1use clap::Parser;
13use tern_core::error::TernResult;
14use tern_core::future::Future;
15use tern_core::migration::MigrationContext;
16use tern_core::runner::{Report, Runner};
17
18mod cli;
19mod commands;
20
21pub trait ContextOptions {
25 type Ctx: MigrationContext;
26
27 fn connect(&self, db_url: &str) -> impl Future<Output = TernResult<Self::Ctx>>;
29}
30
31pub struct App<Opts> {
50 opts: Opts,
51 cli: cli::Tern,
52}
53
54impl<Opts> App<Opts>
55where
56 Opts: ContextOptions,
57{
58 pub fn new(opts: Opts) -> Self {
59 let cli = cli::Tern::parse();
60 Self { opts, cli }
61 }
62
63 pub async fn run(&self) -> anyhow::Result<Option<Report>> {
64 match &self.cli.commands {
65 cli::TernCommands::History(history) => match &history.commands {
66 cli::HistoryCommands::Init { connect_opts } => {
67 let db_url = connect_opts.required_db_url()?.to_string();
68 let context = self.opts.connect(&db_url).await?;
69 let mut runner = Runner::new(context);
70 runner.init_history().await?;
71
72 Ok(None)
73 }
74 cli::HistoryCommands::Drop { connect_opts } => {
75 let db_url = connect_opts.required_db_url()?.to_string();
76 let context = self.opts.connect(&db_url).await?;
77 let mut runner = Runner::new(context);
78 runner.drop_history().await?;
79
80 Ok(None)
81 }
82 cli::HistoryCommands::SoftApply { .. } => Err(anyhow::anyhow!(
83 "Deprecated: use `migrate soft-apply` instead"
84 )),
85 },
86 cli::TernCommands::Migrate(migrate) => match &migrate.commands {
87 cli::MigrateCommands::Apply {
88 dryrun,
89 target_version,
90 connect_opts,
91 } => {
92 let db_url = connect_opts.required_db_url()?.to_string();
93 let context = self.opts.connect(&db_url).await?;
94 let mut runner = Runner::new(context);
95 let report = runner.run_apply(*target_version, *dryrun).await?;
96
97 Ok(Some(report))
98 }
99 cli::MigrateCommands::ApplyAll {
100 dryrun,
101 connect_opts,
102 } => {
103 let db_url = connect_opts.required_db_url()?.to_string();
104 let context = self.opts.connect(&db_url).await?;
105 let mut runner = Runner::new(context);
106 let report = runner.run_apply_all(*dryrun).await?;
107
108 Ok(Some(report))
109 }
110 cli::MigrateCommands::SoftApply {
111 dryrun,
112 target_version,
113 connect_opts,
114 } => {
115 let db_url = connect_opts.required_db_url()?.to_string();
116 let context = self.opts.connect(&db_url).await?;
117 let mut runner = Runner::new(context);
118 let report = runner.run_soft_apply(*target_version, *dryrun).await?;
119
120 Ok(Some(report))
121 }
122 cli::MigrateCommands::ListApplied { connect_opts } => {
123 let db_url = connect_opts.required_db_url()?.to_string();
124 let context = self.opts.connect(&db_url).await?;
125 let mut runner = Runner::new(context);
126 let report = runner.list_applied().await?;
127
128 Ok(Some(report))
129 }
130 cli::MigrateCommands::New {
131 description,
132 no_tx,
133 migration_type,
134 source,
135 } => {
136 commands::new(
137 description.to_string(),
138 *no_tx,
139 *migration_type,
140 source.path.clone(),
141 )?;
142
143 Ok(None)
144 }
145 },
146 }
147 }
148}