use clap::{
builder::{EnumValueParser, PossibleValuesParser},
Arg, Command,
};
use clap_complete::Shell;
pub fn build_cli() -> Command<'static> {
let database_arg = Arg::new("DATABASE_URL")
.long("database-url")
.help(
"Specifies the database URL to connect to. Falls back to \
the DATABASE_URL environment variable if unspecified.",
)
.global(true)
.takes_value(true);
let migration_subcommand = Command::new("migration")
.about(
"A group of commands for generating, running, and reverting \
migrations.",
)
.arg(migration_dir_arg())
.subcommand(Command::new("run").about("Runs all pending migrations."))
.subcommand(
Command::new("revert")
.about("Reverts the specified migrations.")
.arg(
Arg::new("REVERT_ALL")
.long("all")
.short('a')
.help("Reverts previously run migration files.")
.takes_value(false)
.conflicts_with("REVERT_NUMBER"),
)
.arg(
Arg::new("REVERT_NUMBER")
.long("number")
.short('n')
.help("Reverts the last `n` migration files.")
.long_help(
"When this option is specified the last `n` migration files \
will be reverted. By default revert the last one.",
)
.default_value("1")
.takes_value(true)
.value_parser(clap::value_parser!(u64))
.conflicts_with("REVERT_ALL"),
),
)
.subcommand(
Command::new("redo")
.about(
"Reverts and re-runs the latest migration. Useful \
for testing that a migration can in fact be reverted.",
)
.arg(
Arg::new("REDO_ALL")
.long("all")
.short('a')
.help("Reverts and re-runs all migrations.")
.long_help(
"When this option is specified all migrations \
will be reverted and re-runs. Useful for testing \
that your migrations can be reverted and applied.",
)
.takes_value(false)
.conflicts_with("REDO_NUMBER"),
)
.arg(
Arg::new("REDO_NUMBER")
.long("number")
.short('n')
.help("Redo the last `n` migration files.")
.long_help(
"When this option is specified the last `n` migration files \
will be reverted and re-runs. By default redo the last migration.",
)
.default_value("1")
.takes_value(true)
.value_parser(clap::value_parser!(u64))
.conflicts_with("REDO_ALL"),
),
)
.subcommand(
Command::new("list")
.about("Lists all available migrations, marking those that have been applied."),
)
.subcommand(
Command::new("pending").about("Returns true if there are any pending migrations."),
)
.subcommand(
Command::new("generate")
.about(
"Generate a new migration with the given name, and \
the current timestamp as the version.",
)
.arg(
Arg::new("MIGRATION_NAME")
.help("The name of the migration to create.")
.required(true),
)
.arg(
Arg::new("MIGRATION_VERSION")
.long("version")
.help(
"The version number to use when generating the migration. \
Defaults to the current timestamp, which should suffice \
for most use cases.",
)
.takes_value(true),
)
.arg(
Arg::new("MIGRATION_NO_DOWN_FILE")
.short('u') .long("no-down")
.help(
"Don't generate a down.sql file. \
You won't be able to run migration `revert` or `redo`.",
)
.takes_value(false),
)
.arg(
Arg::new("MIGRATION_FORMAT")
.long("format")
.value_parser(PossibleValuesParser::new(["sql"]))
.takes_value(true)
.default_value("sql")
.takes_value(true)
.help("The format of the migration to be generated."),
),
)
.subcommand_required(true)
.arg_required_else_help(true);
let setup_subcommand = Command::new("setup").arg(migration_dir_arg()).about(
"Creates the migrations directory, creates the database \
specified in your DATABASE_URL, and runs existing migrations.",
);
let database_subcommand = Command::new("database")
.alias("db")
.arg(migration_dir_arg())
.about("A group of commands for setting up and resetting your database.")
.subcommand(Command::new("setup").about(
"Creates the database specified in your DATABASE_URL, \
and then runs any existing migrations.",
))
.subcommand(Command::new("reset").about(
"Resets your database by dropping the database specified \
in your DATABASE_URL and then running `diesel database setup`.",
))
.subcommand(
Command::new("drop")
.about("Drops the database specified in your DATABASE_URL.")
.hide(true),
)
.subcommand_required(true)
.arg_required_else_help(true);
let generate_completions_subcommand = Command::new("completions")
.about("Generate shell completion scripts for the diesel command.")
.arg(
Arg::new("SHELL")
.index(1)
.required(true)
.value_parser(EnumValueParser::<Shell>::new()),
);
let infer_schema_subcommand = Command::new("print-schema")
.about("Print table definitions for database schema.")
.arg(
Arg::new("schema")
.long("schema")
.short('s')
.takes_value(true)
.help("The name of the schema."),
)
.arg(
Arg::new("table-name")
.index(1)
.takes_value(true)
.multiple_values(true)
.action(clap::ArgAction::Append)
.help("Table names to filter (default only-tables if not empty)."),
)
.arg(
Arg::new("only-tables")
.short('o')
.long("only-tables")
.help("Only include tables from table-name that matches regexp.")
.conflicts_with("except-tables"),
)
.arg(
Arg::new("except-tables")
.short('e')
.long("except-tables")
.help("Exclude tables from table-name that matches regex.")
.conflicts_with("only-tables"),
)
.arg(
Arg::new("with-docs")
.long("with-docs")
.help("Render documentation comments for tables and columns."),
)
.arg(
Arg::new("column-sorting")
.long("column-sorting")
.help("Sort order for table columns.")
.takes_value(true)
.value_parser(PossibleValuesParser::new(["ordinal_position", "name"])),
)
.arg(
Arg::new("patch-file")
.long("patch-file")
.takes_value(true)
.value_parser(clap::value_parser!(std::path::PathBuf))
.help("A unified diff file to be applied to the final schema."),
)
.arg(
Arg::new("import-types")
.long("import-types")
.takes_value(true)
.multiple_values(true)
.action(clap::ArgAction::Append)
.number_of_values(1)
.help("A list of types to import for every table, separated by commas."),
)
.arg(
Arg::new("generate-custom-type-definitions")
.long("no-generate-missing-sql-type-definitions")
.help("Generate SQL type definitions for types not provided by diesel"),
);
let config_arg = Arg::new("CONFIG_FILE")
.value_parser(clap::value_parser!(std::path::PathBuf))
.long("config-file")
.help(
"The location of the configuration file to use. Falls back to the \
`DIESEL_CONFIG_FILE` environment variable if unspecified. Defaults \
to `diesel.toml` in your project root. See \
diesel.rs/guides/configuring-diesel-cli for documentation on this file.",
)
.global(true)
.takes_value(true);
let locked_schema_arg = Arg::new("LOCKED_SCHEMA")
.long("locked-schema")
.help("Require that the schema file is up to date.")
.long_help(
"When `print_schema.file` is specified in your config file, this \
flag will cause Diesel CLI to error if any command would result in \
changes to that file. It is recommended that you use this flag when \
running migrations in CI or production.",
)
.global(true);
Command::new("diesel")
.version(env!("CARGO_PKG_VERSION"))
.after_help(
"You can also run `diesel SUBCOMMAND -h` to get more information about that subcommand.",
)
.arg(database_arg)
.arg(config_arg)
.arg(locked_schema_arg)
.subcommand(migration_subcommand)
.subcommand(setup_subcommand)
.subcommand(database_subcommand)
.subcommand(generate_completions_subcommand)
.subcommand(infer_schema_subcommand)
.subcommand_required(true)
.arg_required_else_help(true)
}
fn migration_dir_arg<'a>() -> Arg<'a> {
Arg::new("MIGRATION_DIRECTORY")
.long("migration-dir")
.help(
"The location of your migration directory. By default this \
will look for a directory called `migrations` in the \
current directory and its parents.",
)
.takes_value(true)
.value_parser(clap::value_parser!(std::path::PathBuf))
.global(true)
}