Skip to main content

idb/cli/
mod.rs

1//! CLI subcommand implementations for the `inno` binary.
2//!
3//! The `inno` binary provides twelve subcommands for analyzing InnoDB data files,
4//! redo logs, and system tablespaces. CLI argument parsing uses clap derive macros,
5//! with the top-level [`app::Cli`] struct and [`app::Commands`] enum defined in
6//! [`app`] and shared between `main.rs` and `build.rs` (for man page generation)
7//! via `include!()`.
8//!
9//! Each subcommand module follows the same pattern: an `Options` struct holding
10//! the parsed arguments and a `pub fn execute(opts, writer) -> Result<(), IdbError>`
11//! entry point. The `writer: &mut dyn Write` parameter allows output to be
12//! captured in tests or redirected to a file via the global `--output` flag.
13//!
14//! # Subcommands
15//!
16//! | Command | Module | Purpose |
17//! |---------|--------|---------|
18//! | `inno parse` | [`parse`] | Parse FIL headers for every page and show a page-type summary table |
19//! | `inno pages` | [`pages`] | Deep structure analysis of INDEX, UNDO, BLOB/LOB, and SDI pages |
20//! | `inno dump` | [`dump`] | Hex dump of raw bytes by page number or absolute file offset |
21//! | `inno checksum` | [`checksum`] | Validate CRC-32C and legacy InnoDB checksums for every page |
22//! | `inno diff` | [`diff`] | Compare two tablespace files page-by-page and report differences |
23//! | `inno corrupt` | [`corrupt`] | Inject random bytes into a page for testing recovery workflows |
24//! | `inno recover` | [`recover`] | Assess page-level recoverability and count salvageable records |
25//! | `inno find` | [`find`] | Search a MySQL data directory for pages matching a page number |
26//! | `inno tsid` | [`tsid`] | List or look up tablespace (space) IDs across `.ibd`/`.ibu` files |
27//! | `inno sdi` | [`sdi`] | Extract SDI metadata (MySQL 8.0+ serialized data dictionary) |
28//! | `inno log` | [`log`] | Analyze redo log file headers, checkpoints, and data blocks |
29//! | `inno info` | [`info`] | Inspect `ibdata1`, compare LSNs, or query a live MySQL instance |
30//!
31//! # Common patterns
32//!
33//! - **`--json`** — Every subcommand supports structured JSON output via
34//!   `#[derive(Serialize)]` structs and `serde_json`.
35//! - **`--page-size`** — Override auto-detected page size (useful for non-standard
36//!   4K, 8K, 32K, or 64K tablespaces).
37//! - **`--verbose` / `-v`** — Show additional detail such as per-page checksum
38//!   status, FSEG internals, or MLOG record types.
39//! - **`--color`** (global) — Control colored terminal output (`auto`, `always`,
40//!   `never`).
41//! - **`--output` / `-o`** (global) — Redirect output to a file instead of stdout.
42//!
43//! Progress bars (via [`indicatif`]) are displayed for long-running operations
44//! in `parse`, `checksum`, and `find`. The `wprintln!` and `wprint!` macros
45//! wrap `writeln!`/`write!` to convert `io::Error` into `IdbError`.
46
47pub mod app;
48pub mod checksum;
49pub mod corrupt;
50pub mod diff;
51pub mod dump;
52pub mod find;
53pub mod info;
54pub mod log;
55pub mod pages;
56pub mod parse;
57pub mod recover;
58pub mod sdi;
59pub mod tsid;
60
61/// Write a line to the given writer, converting io::Error to IdbError.
62macro_rules! wprintln {
63    ($w:expr) => {
64        writeln!($w).map_err(|e| $crate::IdbError::Io(e.to_string()))
65    };
66    ($w:expr, $($arg:tt)*) => {
67        writeln!($w, $($arg)*).map_err(|e| $crate::IdbError::Io(e.to_string()))
68    };
69}
70
71/// Write (without newline) to the given writer, converting io::Error to IdbError.
72macro_rules! wprint {
73    ($w:expr, $($arg:tt)*) => {
74        write!($w, $($arg)*).map_err(|e| $crate::IdbError::Io(e.to_string()))
75    };
76}
77
78pub(crate) use wprint;
79pub(crate) use wprintln;
80
81use indicatif::{ProgressBar, ProgressStyle};
82
83/// Create a styled progress bar for iterating over pages or files.
84pub(crate) fn create_progress_bar(count: u64, unit: &str) -> ProgressBar {
85    let pb = ProgressBar::new(count);
86    pb.set_style(
87        ProgressStyle::default_bar()
88            .template(&format!(
89                "{{spinner:.green}} [{{bar:40.cyan/blue}}] {{pos}}/{{len}} {} ({{eta}})",
90                unit
91            ))
92            .unwrap()
93            .progress_chars("#>-"),
94    );
95    pb
96}