toasty_cli/
lib.rs

1#![warn(missing_docs)]
2//! A library for building Toasty command-line tools.
3//!
4//! `toasty-cli` provides [`ToastyCli`], a ready-made CLI runner that wraps a
5//! [`toasty::Db`] handle and exposes database migration subcommands (generate,
6//! apply, drop, reset, snapshot). It uses [clap] for argument parsing and
7//! [dialoguer] for interactive prompts.
8//!
9//! The crate also exposes the underlying configuration and file types so that
10//! custom tooling can read and manipulate migration history and snapshots
11//! directly.
12//!
13//! # Main types
14//!
15//! - [`ToastyCli`] — parses CLI arguments and dispatches to the appropriate
16//!   migration subcommand.
17//! - [`Config`] / [`MigrationConfig`] — configure migration paths, prefix
18//!   styles, and checksum behavior. Loaded from a `Toasty.toml` file or built
19//!   programmatically.
20//! - [`HistoryFile`] / [`HistoryFileMigration`] — read and write the TOML
21//!   history that tracks which migrations exist.
22//! - [`SnapshotFile`] — read and write schema snapshot TOML files.
23//!
24//! # Examples
25//!
26//! ```ignore
27//! use toasty_cli::ToastyCli;
28//!
29//! let db = toasty::Db::builder("sqlite::memory:").build().await?;
30//! let cli = ToastyCli::new(db);
31//! cli.parse_and_run().await?;
32//! ```
33
34mod config;
35mod migration;
36mod theme;
37mod utility;
38
39pub use config::*;
40pub use migration::*;
41
42use anyhow::Result;
43use clap::Parser;
44use toasty::Db;
45
46/// A CLI runner that dispatches migration subcommands against a [`Db`].
47///
48/// `ToastyCli` holds a database connection and a [`Config`]. Call
49/// [`parse_and_run`](Self::parse_and_run) to parse `std::env::args` and
50/// execute the matching subcommand, or [`parse_from`](Self::parse_from) to
51/// parse from an arbitrary iterator (useful for testing).
52///
53/// # Examples
54///
55/// ```ignore
56/// use toasty_cli::{ToastyCli, Config, MigrationConfig};
57///
58/// let config = Config::new()
59///     .migration(MigrationConfig::new().path("db"));
60/// let db = toasty::Db::builder("sqlite::memory:").build().await?;
61/// let cli = ToastyCli::with_config(db, config);
62/// cli.parse_from(["toasty", "migration", "apply"]).await?;
63/// ```
64pub struct ToastyCli {
65    db: Db,
66    config: Config,
67}
68
69impl ToastyCli {
70    /// Create a new ToastyCli instance with the given database connection
71    pub fn new(db: Db) -> Self {
72        Self {
73            db,
74            config: Config::default(),
75        }
76    }
77
78    /// Create a new ToastyCli instance with a custom configuration
79    pub fn with_config(db: Db, config: Config) -> Self {
80        Self { db, config }
81    }
82
83    /// Get a reference to the configuration
84    pub fn config(&self) -> &Config {
85        &self.config
86    }
87
88    /// Parse and execute CLI commands from command-line arguments
89    pub async fn parse_and_run(&self) -> Result<()> {
90        let cli = Cli::parse();
91        self.run(cli).await
92    }
93
94    /// Parse and execute CLI commands from an iterator of arguments
95    pub async fn parse_from<I, T>(&self, args: I) -> Result<()>
96    where
97        I: IntoIterator<Item = T>,
98        T: Into<std::ffi::OsString> + Clone,
99    {
100        let cli = Cli::parse_from(args);
101        self.run(cli).await
102    }
103
104    async fn run(&self, cli: Cli) -> Result<()> {
105        match cli.command {
106            Command::Migration(cmd) => cmd.run(&self.db, &self.config).await,
107        }
108    }
109}
110
111#[derive(Parser, Debug)]
112#[command(name = "toasty")]
113#[command(about = "Toasty CLI - Database migration and management tool")]
114#[command(version)]
115struct Cli {
116    #[command(subcommand)]
117    command: Command,
118}
119
120#[derive(Parser, Debug)]
121enum Command {
122    /// Database migration commands
123    Migration(migration::MigrationCommand),
124}