1use clap::Parser;
11use tern_core::error::TernResult;
12use tern_core::future::Future;
13use tern_core::migration::MigrationContext;
14use tern_core::runner::{Report, Runner};
15
16mod cli;
17mod commands;
18
19pub trait ContextOptions {
21 type Ctx: MigrationContext;
22
23 fn connect(&self, db_url: &str) -> impl Future<Output = TernResult<Self::Ctx>>;
25}
26
27pub struct App<T> {
45 inner: T,
46 cli: cli::Tern,
47}
48
49impl<T> App<T> {
50 pub fn new(inner: T) -> Self {
51 let cli = cli::Tern::parse();
52 Self { inner, cli }
53 }
54
55 pub async fn run(&self) -> anyhow::Result<Option<Report>>
58 where
59 T: ContextOptions,
60 {
61 match &self.cli.commands {
62 cli::TernCommands::History(history) => match &history.commands {
63 cli::HistoryCommands::Init { connect_opts } => {
64 let db_url = connect_opts.required_db_url()?.to_string();
65 let context = self.inner.connect(&db_url).await?;
66 let mut runner = Runner::new(context);
67 runner.init_history().await?;
68
69 Ok(None)
70 }
71 cli::HistoryCommands::Drop { connect_opts } => {
72 let db_url = connect_opts.required_db_url()?.to_string();
73 let context = self.inner.connect(&db_url).await?;
74 let mut runner = Runner::new(context);
75 runner.drop_history().await?;
76
77 Ok(None)
78 }
79 cli::HistoryCommands::SoftApply { .. } => Err(anyhow::anyhow!(
80 "Deprecated: use `migrate soft-apply` instead"
81 )),
82 },
83 cli::TernCommands::Migrate(migrate) => match &migrate.commands {
84 cli::MigrateCommands::Apply {
85 dryrun,
86 target_version,
87 connect_opts,
88 } => {
89 let db_url = connect_opts.required_db_url()?.to_string();
90 let context = self.inner.connect(&db_url).await?;
91 let mut runner = Runner::new(context);
92 let report = runner.run_apply(*target_version, *dryrun).await?;
93
94 Ok(Some(report))
95 }
96 cli::MigrateCommands::ApplyAll {
97 dryrun,
98 connect_opts,
99 } => {
100 let db_url = connect_opts.required_db_url()?.to_string();
101 let context = self.inner.connect(&db_url).await?;
102 let mut runner = Runner::new(context);
103 let report = runner.run_apply_all(*dryrun).await?;
104
105 Ok(Some(report))
106 }
107 cli::MigrateCommands::SoftApply {
108 dryrun,
109 target_version,
110 connect_opts,
111 } => {
112 let db_url = connect_opts.required_db_url()?.to_string();
113 let context = self.inner.connect(&db_url).await?;
114 let mut runner = Runner::new(context);
115 let report = runner.run_soft_apply(*target_version, *dryrun).await?;
116
117 Ok(Some(report))
118 }
119 cli::MigrateCommands::ListApplied { connect_opts } => {
120 let db_url = connect_opts.required_db_url()?.to_string();
121 let context = self.inner.connect(&db_url).await?;
122 let mut runner = Runner::new(context);
123 let report = runner.list_applied().await?;
124
125 Ok(Some(report))
126 }
127 cli::MigrateCommands::New {
128 description,
129 no_tx,
130 migration_type,
131 source,
132 } => {
133 commands::new(
134 description.to_string(),
135 *no_tx,
136 *migration_type,
137 source.path.clone(),
138 )?;
139
140 Ok(None)
141 }
142 },
143 }
144 }
145
146 pub async fn run_with_context(self) -> anyhow::Result<Option<Report>>
149 where
150 T: MigrationContext,
151 {
152 let mut runner = Runner::new(self.inner);
153 let cli = self.cli;
154
155 match cli.commands {
156 cli::TernCommands::History(history) => match &history.commands {
157 cli::HistoryCommands::Init { .. } => {
158 runner.init_history().await?;
159
160 Ok(None)
161 }
162 cli::HistoryCommands::Drop { .. } => {
163 runner.drop_history().await?;
164
165 Ok(None)
166 }
167 cli::HistoryCommands::SoftApply { .. } => Err(anyhow::anyhow!(
168 "Deprecated: use `migrate soft-apply` instead"
169 )),
170 },
171 cli::TernCommands::Migrate(migrate) => match migrate.commands {
172 cli::MigrateCommands::Apply {
173 dryrun,
174 target_version,
175 ..
176 } => {
177 let report = runner.run_apply(target_version, dryrun).await?;
178
179 Ok(Some(report))
180 }
181 cli::MigrateCommands::ApplyAll { dryrun, .. } => {
182 let report = runner.run_apply_all(dryrun).await?;
183
184 Ok(Some(report))
185 }
186 cli::MigrateCommands::SoftApply {
187 dryrun,
188 target_version,
189 ..
190 } => {
191 let report = runner.run_soft_apply(target_version, dryrun).await?;
192
193 Ok(Some(report))
194 }
195 cli::MigrateCommands::ListApplied { .. } => {
196 let report = runner.list_applied().await?;
197
198 Ok(Some(report))
199 }
200 cli::MigrateCommands::New {
201 description,
202 no_tx,
203 migration_type,
204 source,
205 } => {
206 commands::new(
207 description.to_string(),
208 no_tx,
209 migration_type,
210 source.path.clone(),
211 )?;
212
213 Ok(None)
214 }
215 },
216 }
217 }
218}