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