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}