cargo-anatomy
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,traitandtypedefinitions). - 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 isconcrete, otherwisemixed. - H (cohesion): > 1.0 is
high, otherwiselow. - I (instability): ≥ 0.7 is
unstable, ≤ 0.3 isstable, otherwisemoderate. - D' (normalized distance):
- ≤ 0.4 →
good. - ≥ 0.6 →
uselessifA + I - 1 ≥ 0, otherwisepainful. - otherwise
balanced.cargo-anatomyalso reports the list of classes discovered in each crate and detailed dependency graphs when invoked with the-aflag.
- ≤ 0.4 →
Usage
# Run on the current workspace
# Show detailed class and dependency information
# Include external dependencies in metrics (may be slower)
# Use a custom evaluation config generated by `cargo anatomy init` below
# Generate a template config file which you can modify and use with `-c`
# Display help with metric descriptions
# Show version
# Output in YAML format
# Output in Graphviz DOT format
# Output in Mermaid format
# Fail if metrics violate thresholds (experimental)
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.
See docs/output-schema.md for a description of the output schema. Example output (| jq):
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:
[]
[]
= 0.7 # minimum ratio considered abstract
= 0.3 # maximum ratio considered concrete
[]
= 1.0 # values greater than this are high cohesion
[]
= 0.7 # minimum ratio considered unstable
= 0.3 # maximum ratio considered stable
[]
= 0.4 # max normalized distance considered good
= 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:
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:
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:
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:
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