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(
25 &self,
26 db_url: &str,
27 ) -> impl Future<Output = TernResult<Self::Ctx>>;
28}
29
30pub struct App<T> {
48 inner: T,
49 cli: cli::Tern,
50}
51
52impl<T> App<T> {
53 pub fn new(inner: T) -> Self {
54 let cli = cli::Tern::parse();
55 Self { inner, cli }
56 }
57
58 pub async fn run(&self) -> anyhow::Result<Option<Report>>
61 where
62 T: ContextOptions,
63 {
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.inner.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.inner.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.inner.connect(&db_url).await?;
94 let mut runner = Runner::new(context);
95 let report =
96 runner.run_apply(*target_version, *dryrun).await?;
97
98 Ok(Some(report))
99 },
100 cli::MigrateCommands::ApplyAll { dryrun, connect_opts } => {
101 let db_url = connect_opts.required_db_url()?.to_string();
102 let context = self.inner.connect(&db_url).await?;
103 let mut runner = Runner::new(context);
104 let report = runner.run_apply_all(*dryrun).await?;
105
106 Ok(Some(report))
107 },
108 cli::MigrateCommands::SoftApply {
109 dryrun,
110 target_version,
111 connect_opts,
112 } => {
113 let db_url = connect_opts.required_db_url()?.to_string();
114 let context = self.inner.connect(&db_url).await?;
115 let mut runner = Runner::new(context);
116 let report =
117 runner.run_soft_apply(*target_version, *dryrun).await?;
118
119 Ok(Some(report))
120 },
121 cli::MigrateCommands::ListApplied { connect_opts } => {
122 let db_url = connect_opts.required_db_url()?.to_string();
123 let context = self.inner.connect(&db_url).await?;
124 let mut runner = Runner::new(context);
125 let report = runner.list_applied().await?;
126
127 Ok(Some(report))
128 },
129 cli::MigrateCommands::New {
130 description,
131 no_tx,
132 migration_type,
133 source,
134 } => {
135 commands::new(
136 description.to_string(),
137 *no_tx,
138 *migration_type,
139 source.path.clone(),
140 )?;
141
142 Ok(None)
143 },
144 },
145 }
146 }
147
148 pub async fn run_with_context(self) -> anyhow::Result<Option<Report>>
151 where
152 T: MigrationContext,
153 {
154 let mut runner = Runner::new(self.inner);
155 let cli = self.cli;
156
157 match cli.commands {
158 cli::TernCommands::History(history) => match &history.commands {
159 cli::HistoryCommands::Init { .. } => {
160 runner.init_history().await?;
161
162 Ok(None)
163 },
164 cli::HistoryCommands::Drop { .. } => {
165 runner.drop_history().await?;
166
167 Ok(None)
168 },
169 cli::HistoryCommands::SoftApply { .. } => Err(anyhow::anyhow!(
170 "Deprecated: use `migrate soft-apply` instead"
171 )),
172 },
173 cli::TernCommands::Migrate(migrate) => match migrate.commands {
174 cli::MigrateCommands::Apply {
175 dryrun, target_version, ..
176 } => {
177 let report =
178 runner.run_apply(target_version, dryrun).await?;
179
180 Ok(Some(report))
181 },
182 cli::MigrateCommands::ApplyAll { dryrun, .. } => {
183 let report = runner.run_apply_all(dryrun).await?;
184
185 Ok(Some(report))
186 },
187 cli::MigrateCommands::SoftApply {
188 dryrun,
189 target_version,
190 ..
191 } => {
192 let report =
193 runner.run_soft_apply(target_version, dryrun).await?;
194
195 Ok(Some(report))
196 },
197 cli::MigrateCommands::ListApplied { .. } => {
198 let report = runner.list_applied().await?;
199
200 Ok(Some(report))
201 },
202 cli::MigrateCommands::New {
203 description,
204 no_tx,
205 migration_type,
206 source,
207 } => {
208 commands::new(
209 description.to_string(),
210 no_tx,
211 migration_type,
212 source.path.clone(),
213 )?;
214
215 Ok(None)
216 },
217 },
218 }
219 }
220}