cargo-treemap 0.1.0

Interactive treemap analyzer for Rust binary size and dependency API usage — like webpack-bundle-analyzer, for cargo.
//! Command-line surface. `cargo-treemap` is dispatched by cargo as `cargo treemap`,
//! which injects `treemap` as argv[1]; the wrapper enum consumes that token.

use clap::Parser;
use std::path::PathBuf;

#[derive(Parser)]
#[command(name = "cargo", bin_name = "cargo")]
#[command(styles = clap_cargo::style::CLAP_STYLING)]
pub enum CargoCli {
    /// Interactive treemap of Rust binary size & dependency usage.
    Treemap(TreemapArgs),
}

#[derive(clap::Args)]
#[command(version, about = "Interactive treemap of Rust binary size & dependency usage")]
pub struct TreemapArgs {
    // cargo-inherited flag groups (do not hand-roll these).
    #[command(flatten)]
    pub manifest: clap_cargo::Manifest,
    #[command(flatten)]
    pub workspace: clap_cargo::Workspace,
    #[command(flatten)]
    pub features: clap_cargo::Features,

    /// Analyze a pre-built binary/library directly instead of running a build. Repeatable.
    /// Crate names are derived from the binary's own symbols (no rlib map, so attribution is
    /// heuristic). Skips --api-usage.
    #[arg(long)]
    pub artifact: Vec<PathBuf>,

    /// Build for the given target triple (e.g. x86_64-unknown-linux-gnu, wasm32-unknown-unknown).
    #[arg(long)]
    pub target: Option<String>,
    /// Analyze the release profile.
    #[arg(long)]
    pub release: bool,
    /// Analyze a named profile (overrides --release).
    #[arg(long)]
    pub profile: Option<String>,
    /// Only analyze the given binary target(s).
    #[arg(long)]
    pub bin: Vec<String>,
    /// Analyze the library target (cdylib/staticlib artifacts).
    #[arg(long)]
    pub lib: bool,

    /// Output format.
    #[arg(long, value_enum, default_value_t = Format::Html)]
    pub format: Format,
    /// Output path (HTML report / JSON). Defaults to ./cargo-treemap.html for HTML.
    #[arg(long, short)]
    pub output: Option<PathBuf>,
    /// Open the generated HTML report in the default browser.
    #[arg(long)]
    pub open: bool,

    /// Which metric the treemap is initially weighted by.
    #[arg(long, value_enum, default_value_t = Metric::Text)]
    pub metric: Metric,
    /// Break the standard library into its component crates instead of one `std` bucket.
    #[arg(long)]
    pub split_std: bool,

    /// Attribute symbols by DWARF debug info (address to source file to crate) instead of by
    /// symbol name. More accurate, reclaims anonymous rodata from the `[runtime]` bucket, and
    /// forces a debug build.
    #[arg(long)]
    pub dwarf: bool,

    /// Measure per-crate compile time via `cargo build --timings`. Does a full from-scratch
    /// build in a separate target dir, so it's slow every run.
    #[arg(long)]
    pub compile_time: bool,

    /// Compare the current build against a baseline saved earlier with `--format json`, and
    /// report the per-crate/per-symbol size delta.
    #[arg(long, value_name = "BASE.json")]
    pub compare: Option<PathBuf>,

    /// Compare the current build against a git ref (branch, tag, or commit). Builds the ref in
    /// a temporary worktree, so the working tree is untouched. Overrides --compare.
    #[arg(long, value_name = "REF")]
    pub compare_ref: Option<String>,

    /// Attribute binary size to each of the package's Cargo features by building with each one
    /// toggled on against a no-default-features baseline. One build per feature, so it's slow.
    #[arg(long)]
    pub attribute_features: bool,

    /// Compute the API-usage "how dependent are you" metric for each direct dependency.
    /// Generates rustdoc JSON per dependency, so it's slower.
    #[arg(long)]
    pub api_usage: bool,

    /// With --api-usage, do an extra opt-level-0 (`v0`-mangled) build so cross-crate calls
    /// aren't inlined away, giving a truer "used" count. Doubles build time.
    #[arg(long, requires = "api_usage")]
    pub accurate: bool,
}

#[derive(clap::ValueEnum, Clone, Copy, PartialEq, Eq)]
pub enum Format {
    Html,
    Json,
    Table,
}

#[derive(clap::ValueEnum, Clone, Copy, PartialEq, Eq)]
pub enum Metric {
    /// Bytes in executable-code sections (`.text`), the reference "parsed size".
    Text,
    /// Bytes in data/read-only-data sections.
    Data,
    /// Gzipped size of each symbol's bytes.
    Gzip,
    /// Number of symbols/monomorphizations aggregated.
    Mono,
    /// Source size per crate (the webpack "stat" analog).
    Stat,
}

impl Metric {
    /// The JSON field name the treemap reads for this metric.
    pub fn field(self) -> &'static str {
        match self {
            Metric::Text => "text",
            Metric::Data => "data",
            Metric::Gzip => "gzip",
            Metric::Mono => "count",
            Metric::Stat => "stat",
        }
    }
}