sqlx_cli/
opt.rs

1use std::ops::{Deref, Not};
2
3use clap::{
4    builder::{styling::AnsiColor, Styles},
5    Args, Parser,
6};
7#[cfg(feature = "completions")]
8use clap_complete::Shell;
9
10const HELP_STYLES: Styles = Styles::styled()
11    .header(AnsiColor::Blue.on_default().bold())
12    .usage(AnsiColor::Blue.on_default().bold())
13    .literal(AnsiColor::White.on_default())
14    .placeholder(AnsiColor::Green.on_default());
15
16#[derive(Parser, Debug)]
17#[clap(version, about, author, styles = HELP_STYLES)]
18pub struct Opt {
19    // https://github.com/launchbadge/sqlx/pull/3724 placed this here,
20    // but the intuitive place would be in the arguments for each subcommand.
21    #[clap(flatten)]
22    pub no_dotenv: NoDotenvOpt,
23
24    #[clap(subcommand)]
25    pub command: Command,
26}
27
28#[derive(Parser, Debug)]
29pub enum Command {
30    #[clap(alias = "db")]
31    Database(DatabaseOpt),
32
33    /// Generate query metadata to support offline compile-time verification.
34    ///
35    /// Saves metadata for all invocations of `query!` and related macros to a `.sqlx` directory
36    /// in the current directory (or workspace root with `--workspace`), overwriting if needed.
37    ///
38    /// During project compilation, the absence of the `DATABASE_URL` environment variable or
39    /// the presence of `SQLX_OFFLINE` (with a value of `true` or `1`) will constrain the
40    /// compile-time verification to only read from the cached query metadata.
41    #[clap(alias = "prep")]
42    Prepare {
43        /// Run in 'check' mode. Exits with 0 if the query metadata is up-to-date. Exits with
44        /// 1 if the query metadata needs updating.
45        #[clap(long)]
46        check: bool,
47
48        /// Prepare query macros in dependencies that exist outside the current crate or workspace.
49        #[clap(long)]
50        all: bool,
51
52        /// Generate a single workspace-level `.sqlx` folder.
53        ///
54        /// This option is intended for workspaces where multiple crates use SQLx. If there is only
55        /// one, it is better to run `cargo sqlx prepare` without this option inside that crate.
56        #[clap(long)]
57        workspace: bool,
58
59        /// Arguments to be passed to `cargo rustc ...`.
60        #[clap(last = true)]
61        args: Vec<String>,
62
63        #[clap(flatten)]
64        connect_opts: ConnectOpts,
65    },
66
67    #[clap(alias = "mig")]
68    Migrate(MigrateOpt),
69
70    #[cfg(feature = "completions")]
71    /// Generate shell completions for the specified shell
72    Completions { shell: Shell },
73}
74
75/// Group of commands for creating and dropping your database.
76#[derive(Parser, Debug)]
77pub struct DatabaseOpt {
78    #[clap(subcommand)]
79    pub command: DatabaseCommand,
80}
81
82#[derive(Parser, Debug)]
83pub enum DatabaseCommand {
84    /// Creates the database specified in your DATABASE_URL.
85    Create {
86        #[clap(flatten)]
87        connect_opts: ConnectOpts,
88    },
89
90    /// Drops the database specified in your DATABASE_URL.
91    Drop {
92        #[clap(flatten)]
93        confirmation: Confirmation,
94
95        #[clap(flatten)]
96        connect_opts: ConnectOpts,
97
98        /// PostgreSQL only: force drops the database.
99        #[clap(long, short, default_value = "false")]
100        force: bool,
101    },
102
103    /// Drops the database specified in your DATABASE_URL, re-creates it, and runs any pending migrations.
104    Reset {
105        #[clap(flatten)]
106        confirmation: Confirmation,
107
108        #[clap(flatten)]
109        source: Source,
110
111        #[clap(flatten)]
112        connect_opts: ConnectOpts,
113
114        /// PostgreSQL only: force drops the database.
115        #[clap(long, short, default_value = "false")]
116        force: bool,
117    },
118
119    /// Creates the database specified in your DATABASE_URL and runs any pending migrations.
120    Setup {
121        #[clap(flatten)]
122        source: Source,
123
124        #[clap(flatten)]
125        connect_opts: ConnectOpts,
126    },
127}
128
129/// Group of commands for creating and running migrations.
130#[derive(Parser, Debug)]
131pub struct MigrateOpt {
132    #[clap(subcommand)]
133    pub command: MigrateCommand,
134}
135
136#[derive(Parser, Debug)]
137pub enum MigrateCommand {
138    /// Create a new migration with the given description.
139    ///
140    /// A version number will be automatically assigned to the migration.
141    ///
142    /// For convenience, this command will attempt to detect if sequential versioning is in use,
143    /// and if so, continue the sequence.
144    ///
145    /// Sequential versioning is inferred if:
146    ///
147    /// * The version numbers of the last two migrations differ by exactly 1, or:
148    ///
149    /// * only one migration exists and its version number is either 0 or 1.
150    ///
151    /// Otherwise timestamp versioning is assumed.
152    ///
153    /// This behavior can overridden by `--sequential` or `--timestamp`, respectively.
154    Add {
155        description: String,
156
157        #[clap(flatten)]
158        source: Source,
159
160        /// If true, creates a pair of up and down migration files with same version
161        /// else creates a single sql file
162        #[clap(short)]
163        reversible: bool,
164
165        /// If set, use timestamp versioning for the new migration. Conflicts with `--sequential`.
166        #[clap(short, long)]
167        timestamp: bool,
168
169        /// If set, use sequential versioning for the new migration. Conflicts with `--timestamp`.
170        #[clap(short, long, conflicts_with = "timestamp")]
171        sequential: bool,
172    },
173
174    /// Run all pending migrations.
175    Run {
176        #[clap(flatten)]
177        source: Source,
178
179        /// List all the migrations to be run without applying
180        #[clap(long)]
181        dry_run: bool,
182
183        #[clap(flatten)]
184        ignore_missing: IgnoreMissing,
185
186        #[clap(flatten)]
187        connect_opts: ConnectOpts,
188
189        /// Apply migrations up to the specified version. If unspecified, apply all
190        /// pending migrations. If already at the target version, then no-op.
191        #[clap(long)]
192        target_version: Option<i64>,
193    },
194
195    /// Revert the latest migration with a down file.
196    Revert {
197        #[clap(flatten)]
198        source: Source,
199
200        /// List the migration to be reverted without applying
201        #[clap(long)]
202        dry_run: bool,
203
204        #[clap(flatten)]
205        ignore_missing: IgnoreMissing,
206
207        #[clap(flatten)]
208        connect_opts: ConnectOpts,
209
210        /// Revert migrations down to the specified version. If unspecified, revert
211        /// only the last migration. Set to 0 to revert all migrations. If already
212        /// at the target version, then no-op.
213        #[clap(long)]
214        target_version: Option<i64>,
215    },
216
217    /// List all available migrations.
218    Info {
219        #[clap(flatten)]
220        source: Source,
221
222        #[clap(flatten)]
223        connect_opts: ConnectOpts,
224    },
225
226    /// Generate a `build.rs` to trigger recompilation when a new migration is added.
227    ///
228    /// Must be run in a Cargo project root.
229    BuildScript {
230        #[clap(flatten)]
231        source: Source,
232
233        /// Overwrite the build script if it already exists.
234        #[clap(long)]
235        force: bool,
236    },
237}
238
239/// Argument for the migration scripts source.
240#[derive(Args, Debug)]
241pub struct Source {
242    /// Path to folder containing migrations.
243    #[clap(long, default_value = "migrations")]
244    source: String,
245}
246
247impl Deref for Source {
248    type Target = String;
249
250    fn deref(&self) -> &Self::Target {
251        &self.source
252    }
253}
254
255/// Argument for the database URL.
256#[derive(Args, Debug)]
257pub struct ConnectOpts {
258    #[clap(flatten)]
259    pub no_dotenv: NoDotenvOpt,
260
261    /// Location of the DB, by default will be read from the DATABASE_URL env var or `.env` files.
262    #[clap(long, short = 'D', env)]
263    pub database_url: Option<String>,
264
265    /// The maximum time, in seconds, to try connecting to the database server before
266    /// returning an error.
267    #[clap(long, default_value = "10")]
268    pub connect_timeout: u64,
269
270    /// Set whether or not to create SQLite databases in Write-Ahead Log (WAL) mode:
271    /// https://www.sqlite.org/wal.html
272    ///
273    /// WAL mode is enabled by default for SQLite databases created by `sqlx-cli`.
274    ///
275    /// However, if your application sets a `journal_mode` on `SqliteConnectOptions` to something
276    /// other than `Wal`, then it will have to take the database file out of WAL mode on connecting,
277    /// which requires an exclusive lock and may return a `database is locked` (`SQLITE_BUSY`) error.
278    #[cfg(feature = "_sqlite")]
279    #[clap(long, action = clap::ArgAction::Set, default_value = "true")]
280    pub sqlite_create_db_wal: bool,
281}
282
283#[derive(Args, Debug)]
284pub struct NoDotenvOpt {
285    /// Do not automatically load `.env` files.
286    #[clap(long)]
287    // Parsing of this flag is actually handled _before_ calling Clap,
288    // by `crate::maybe_apply_dotenv()`.
289    #[allow(unused)] // TODO: switch to `#[expect]`
290    pub no_dotenv: bool,
291}
292
293impl ConnectOpts {
294    /// Require a database URL to be provided, otherwise
295    /// return an error.
296    pub fn required_db_url(&self) -> anyhow::Result<&str> {
297        self.database_url.as_deref().ok_or_else(
298            || anyhow::anyhow!(
299                "the `--database-url` option or the `DATABASE_URL` environment variable must be provided"
300            )
301        )
302    }
303}
304
305/// Argument for automatic confirmation.
306#[derive(Args, Copy, Clone, Debug)]
307pub struct Confirmation {
308    /// Automatic confirmation. Without this option, you will be prompted before dropping
309    /// your database.
310    #[clap(short)]
311    pub yes: bool,
312}
313
314/// Argument for ignoring applied migrations that were not resolved.
315#[derive(Args, Copy, Clone, Debug)]
316pub struct IgnoreMissing {
317    /// Ignore applied migrations that are missing in the resolved migrations
318    #[clap(long)]
319    ignore_missing: bool,
320}
321
322impl Deref for IgnoreMissing {
323    type Target = bool;
324
325    fn deref(&self) -> &Self::Target {
326        &self.ignore_missing
327    }
328}
329
330impl Not for IgnoreMissing {
331    type Output = bool;
332
333    fn not(self) -> Self::Output {
334        !self.ignore_missing
335    }
336}