tern_cli/
cli.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use clap::{Args, Parser, ValueEnum};
use std::path::PathBuf;

#[derive(Debug, Parser)]
pub struct Tern {
    #[clap(subcommand)]
    pub commands: TernCommands,
}

#[derive(Debug, Parser)]
pub enum TernCommands {
    Migrate(Migrate),
    History(History),
}

/// Operations on the set of migration files.
#[derive(Debug, Parser)]
pub struct Migrate {
    #[clap(subcommand)]
    pub commands: MigrateCommands,
}

/// Operations on the table storing the history of these migrations.
#[derive(Debug, Parser)]
pub struct History {
    #[clap(subcommand)]
    pub commands: HistoryCommands,
}

#[derive(Debug, Parser)]
pub enum MigrateCommands {
    /// Run any available unapplied migrations.
    ApplyAll {
        /// List the migrations to be applied without applying them.
        #[arg(short, long)]
        dryrun: bool,
        #[clap(flatten)]
        connect_opts: ConnectOpts,
    },
    /// List previously applied migrations.
    ListApplied {
        #[clap(flatten)]
        connect_opts: ConnectOpts,
    },
    /// Create a new migration with an auto-selected version and the given
    /// description.
    New {
        description: String,
        /// If `true`, the annotation for not running the migration in a
        /// transaction will be added to the generated file.
        no_tx: bool,
        /// Whether to create a SQL or Rust migration.
        #[arg(long = "type", value_enum)]
        migration_type: MigrationType,
        #[clap(flatten)]
        source: Source,
    },
}

#[derive(Debug, Parser)]
pub enum HistoryCommands {
    /// Create the schema history table.
    Init {
        #[clap(flatten)]
        connect_opts: ConnectOpts,
    },
    /// Drop the schema history table.
    Drop {
        #[clap(flatten)]
        connect_opts: ConnectOpts,
    },
    /// Do a "soft" apply of all migrations in the specified range.
    /// A soft apply will add the migration to the schema history table but
    /// without running the query for the migration.
    SoftApply {
        /// The version to start the soft apply with.
        /// If not provided, the first migration is where the soft apply starts.
        #[arg(long)]
        from_version: Option<i64>,
        /// The version to end the soft apply with.
        /// If not provided, the last migration is where the soft apply ends.
        #[arg(long)]
        to_version: Option<i64>,
        #[clap(flatten)]
        connect_opts: ConnectOpts,
    },
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
pub enum MigrationType {
    Sql,
    Rust,
}

#[derive(Debug, Args)]
pub struct Source {
    /// Path to the folder containing migrations.
    #[clap(long)]
    pub path: PathBuf,
}

#[derive(Debug, Args)]
pub struct ConnectOpts {
    /// Connection string for the database either from the command line or from
    /// the environment variable `DATABASE_URL`.
    #[clap(long, short = 'D', env)]
    pub database_url: Option<String>,
}

impl ConnectOpts {
    pub fn required_db_url(&self) -> anyhow::Result<&str> {
        self.database_url.as_deref().ok_or_else(
            || anyhow::anyhow!(
                "the `--database-url` option or the `DATABASE_URL` environment variable must be provided"
            )
        )
    }
}