cargo-anatomy

Crates.iocargo-anatomy
lib.rscargo-anatomy
version0.6.8
created_at2025-07-16 04:07:54.802779+00
updated_at2026-01-17 02:38:47.607579+00
descriptionAnalyze Rust workspaces and report package metrics
homepagehttps://github.com/cutsea110/cargo-anatomy
repositoryhttps://github.com/cutsea110/cargo-anatomy
max_upload_size
id1754882
size219,832
cutsea110 (cutsea110)

documentation

README

cargo-anatomy

Cargo Anatomy Logo

Rust CI Crates.io Crates.io Downloads Docker Cloud Build Status

cargo-anatomy analyzes Rust workspaces and calculates metrics inspired by Robert C. Martin's package metrics. Each crate inside the workspace is treated as a package.

Installation

cargo install cargo-anatomy

Metrics

  • N — number of classes in the crate (struct, enum, trait and type definitions).
  • R — number of internal class relationships. Each unique reference from one class to another within the same crate counts once.
  • H — relational cohesion: H = (R + 1) / N.
  • Ca — afferent coupling: the number of external classes that depend on types from this crate.
  • Ce — efferent coupling: the number of classes in this crate that depend on types from other crates in the workspace.
  • A — abstraction: ratio of abstract classes (traits) to total classes, A = abstract_classes / N.
  • I — instability: I = Ce / (Ce + Ca).
  • D — distance from the main sequence: D = |A + I - 1| / sqrt(2).
  • D' — normalized distance from the main sequence: D' = |A + I - 1|.

Evaluation

Each metric is also mapped to a qualitative label. These labels are assigned using the following thresholds:

  • A (abstraction): ≥ 0.7 is abstract, ≤ 0.3 is concrete, otherwise mixed.
  • H (cohesion): > 1.0 is high, otherwise low.
  • I (instability): ≥ 0.7 is unstable, ≤ 0.3 is stable, otherwise moderate.
  • D' (normalized distance):
    • ≤ 0.4 → good.
    • ≥ 0.6 → useless if A + I - 1 ≥ 0, otherwise painful.
    • otherwise balanced. cargo-anatomy also reports the list of classes discovered in each crate and detailed dependency graphs when invoked with the -a flag.

Usage

# Run on the current workspace
cargo anatomy

# Analyze a specific manifest
cargo anatomy --manifest-path path/to/Cargo.toml

# Show detailed class and dependency information
cargo anatomy -a

# Include external dependencies in metrics (may be slower)
cargo anatomy -x

# Use a custom evaluation config generated by `cargo anatomy init` below
cargo anatomy -c config.toml

# Generate a template config file which you can modify and use with `-c`
cargo anatomy init

# Display help with metric descriptions
cargo anatomy -?

# Show version
cargo anatomy -V

# Output in YAML format
cargo anatomy -o yaml
# Output in Graphviz DOT format
cargo anatomy -o dot
# Output in Mermaid format
cargo anatomy -o mermaid

# Fail if metrics violate thresholds (experimental)
cargo anatomy --h-lt 1.1 --d-prime-ge 0.9

For custom evaluation thresholds, run cargo anatomy init to generate a template configuration and pass -c <FILE> to load it. See the Configuration section for more details.

The command outputs metrics for every member crate in compact JSON format by default. Use -x to also analyze external dependencies. Analyzing external crates can significantly increase processing time. When the -a flag is used, each crate also includes a details.kind field indicating whether it is part of the workspace or an external crate. Pipe to jq if you want it pretty printed. Use -o yaml for YAML output, -o dot for Graphviz or -o mermaid for Mermaid diagrams. When using -o dot, you can write the graph to a file and convert it with Graphviz: cargo anatomy -o dot > graph.dot && dot -Tpng graph.dot -o graph.png. Dependency arrows are labeled with the efferent couple count from the source crate when the -a flag is used. Graphviz and Mermaid diagrams can include individual type nodes when --show-types-crates=<CRATE1,CRATE2> is specified. Mermaid will only render the first 500 edges by default, so increase maxEdges in your Mermaid configuration if your graph is larger. Both Graphviz and Mermaid outputs omit dependency edges unless -a is supplied, so combining these formats with -a is recommended when you want to visualize the graph. Use --show-types-crates-all to expand all workspace crates with type details (experimental).

Analyzing macro-generated code

cargo-anatomy only sees the code that exists in your source files. Dependencies introduced by macros are not visible unless you expand them first. You can use cargo expand to generate expanded sources and analyze those instead.

# Install cargo-expand if you don't have it
cargo install cargo-expand

# Produce expanded sources for a crate
cargo expand -p my_crate --lib > /tmp/my_crate/src/lib.rs
cp my_crate/Cargo.toml /tmp/my_crate/
# If the crate inherits dependencies from the workspace, also copy the workspace root
# Cargo.toml or replace `workspace = true` entries with explicit versions.

# Run cargo-anatomy on the expanded copy
cargo anatomy --manifest-path /tmp/my_crate/Cargo.toml

Repeat these steps for any crates that rely heavily on macros to ensure macro-generated types are included in the analysis.

If the copied manifest contains dependencies like anyhow = { workspace = true }, ensure the workspace root Cargo.toml is copied alongside or replace those entries with concrete versions; otherwise cargo metadata will report "failed to find a workspace root".

See docs/output-schema.md for a description of the output schema. Example output (| jq):

{
  "meta": {
  "cargo-anatomy": { "version": "0.6.8", "target": "linux/x86_64" },
    "config": {
      "evaluation": {
        "abstraction": { "abstract_min": 0.7, "concrete_max": 0.3 },
        "cohesion": { "high_gt": 1.0 },
        "instability": { "unstable_min": 0.7, "stable_max": 0.3 },
        "distance": { "good_max": 0.4, "bad_min": 0.6 }
      }
    }
  },
  "crates": [
    {
      "crate_name": "my_crate",
      "metrics": {
        "n": 3,
        "r": 1,
        "h": 0.67,
        "ca": 0,
        "ce": 1,
        "a": 0.33,
        "i": 1.0,
        "d": 0.24,
        "d_prime": 0.34
      },
      "evaluation": {
        "a": "mixed",
        "h": "low",
        "i": "unstable",
        "d_prime": "good"
      },
      "details": {
        "kind": "Workspace",
        "classes": [
          { "name": "Foo", "kind": "Struct" },
          { "name": "Bar", "kind": "Struct" },
          { "name": "MyTrait", "kind": "Trait" }
        ]
      }
    }
  ],
  "warnings": {
    "dependency_cycles": []
  }
}

Enable RUST_LOG=info to see progress logs during analysis.

Configuration

cargo-anatomy looks for a .anatomy.toml file at the workspace root to customize how metric values are evaluated. Run cargo anatomy init to create a template .anatomy.toml in the current directory. Pass -c <FILE> to use a different configuration. The file is written in TOML and contains the following sections and defaults:

[evaluation]
  [evaluation.abstraction]
  abstract_min = 0.7   # minimum ratio considered abstract
  concrete_max = 0.3   # maximum ratio considered concrete

  [evaluation.cohesion]
  high_gt = 1.0        # values greater than this are high cohesion

  [evaluation.instability]
  unstable_min = 0.7   # minimum ratio considered unstable
  stable_max = 0.3     # maximum ratio considered stable

  [evaluation.distance]
  good_max = 0.4       # max normalized distance considered good
  bad_min = 0.6        # min normalized distance considered bad

Specify a path with -c <FILE> to use a different configuration, or omit the option to rely on these defaults.

Docker image

Using the official image

A pre-built container is available on Docker Hub. Replace <version> with the desired tag and mount your workspace into /work:

docker run --rm -v $(pwd):/work cutsea110/cargo-anatomy:<version> [ARGS...]

Any arguments after the image name are forwarded to cargo-anatomy. The image includes the toolchain cargo binary and sets the CARGO environment variable to that path, so cargo metadata works without rustup.

Building an image (for developers)

Build an image for the current architecture and load it into Docker with:

docker buildx build --platform <arch> -t cargo-anatomy --load .

Replace <arch> with linux/amd64 on x86_64 machines or linux/arm64 on Arm-based hosts. To publish a multi-platform image, use --push instead of --load:

docker buildx build --platform linux/amd64,linux/arm64 \
    -t <your-registry>/cargo-anatomy:<version> --push .

Set <version> to the tag for the published image. After building or pulling an image, run it as shown above. The runtime uses a distroless base for a smaller footprint.

GitHub Actions

You can integrate cargo-anatomy in your CI pipeline. The example below installs cargo-anatomy and fails the job when metrics do not satisfy the configured thresholds. Make sure a .anatomy.toml file is present in your repository so the workflow can load your evaluation settings. You can generate a template with cargo anatomy init and commit it to version control.

# .github/workflows/anatomy.yml
name: cargo-anatomy

on:
  push:
    branches: [ main ]
  pull_request:

jobs:
  analyze:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-rust@v1
        with:
          rust-version: stable
      - name: Install cargo-anatomy
        run: cargo install cargo-anatomy
      - name: Run cargo-anatomy
        run: cargo anatomy -c .anatomy.toml --h-lt 1.1 --d-prime-ge 0.9
Commit count: 315

cargo fmt