Skip to main content

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//! - [`toasty::migration::History`] / [`toasty::migration::HistoryEntry`] —
21//!   read and write the TOML 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::Config;
40pub use migration::{
41    ApplyCommand, DropCommand, GenerateCommand, MigrationCommand, MigrationConfig,
42    MigrationPrefixStyle, ResetCommand, SnapshotCommand, SnapshotFile,
43};
44
45use anyhow::Result;
46use clap::Parser;
47use toasty::Db;
48
49/// A CLI runner that dispatches migration subcommands against a [`Db`].
50///
51/// `ToastyCli` holds a database connection and a [`Config`]. Call
52/// [`parse_and_run`](Self::parse_and_run) to parse `std::env::args` and
53/// execute the matching subcommand, or [`parse_from`](Self::parse_from) to
54/// parse from an arbitrary iterator (useful for testing).
55///
56/// # Examples
57///
58/// ```ignore
59/// use toasty_cli::{ToastyCli, Config, MigrationConfig};
60///
61/// let config = Config::new()
62///     .migration(MigrationConfig::new().path("db"));
63/// let db = toasty::Db::builder("sqlite::memory:").build().await?;
64/// let cli = ToastyCli::with_config(db, config);
65/// cli.parse_from(["toasty", "migration", "apply"]).await?;
66/// ```
67pub struct ToastyCli {
68    db: Db,
69    config: Config,
70}
71
72impl ToastyCli {
73    /// Create a new ToastyCli instance with the given database connection
74    pub fn new(db: Db) -> Self {
75        Self {
76            db,
77            config: Config::default(),
78        }
79    }
80
81    /// Create a new ToastyCli instance with a custom configuration
82    pub fn with_config(db: Db, config: Config) -> Self {
83        Self { db, config }
84    }
85
86    /// Get a reference to the configuration
87    pub fn config(&self) -> &Config {
88        &self.config
89    }
90
91    /// Parse and execute CLI commands from command-line arguments
92    pub async fn parse_and_run(&self) -> Result<()> {
93        let cli = Cli::parse();
94        self.run(cli).await
95    }
96
97    /// Parse and execute CLI commands from an iterator of arguments
98    pub async fn parse_from<I, T>(&self, args: I) -> Result<()>
99    where
100        I: IntoIterator<Item = T>,
101        T: Into<std::ffi::OsString> + Clone,
102    {
103        let cli = Cli::parse_from(args);
104        self.run(cli).await
105    }
106
107    async fn run(&self, cli: Cli) -> Result<()> {
108        match cli.command {
109            Command::Migration(cmd) => cmd.run(&self.db, &self.config).await,
110        }
111    }
112}
113
114#[derive(Parser, Debug)]
115#[command(name = "toasty")]
116#[command(about = "Toasty CLI - Database migration and management tool")]
117#[command(version)]
118struct Cli {
119    #[command(subcommand)]
120    command: Command,
121}
122
123#[derive(Parser, Debug)]
124enum Command {
125    /// Database migration commands
126    Migration(migration::MigrationCommand),
127}