Universal introspection and semantic terminal rendering for Rust.
Inspect Rust values as traversable structured data without coupling your application
to a debugger, serializer, UI framework, or runtime reflection engine.
|
Scalar values and strings borrow directly from target memory without eager allocations. Child structures evaluate on-demand via lightweight closures. |
Semantic syntax highlighting communicating structure—types, fields, enums, variants, and secrets. Automatic TTY, |
|
First-class support for |
Configurable limits for recursion depth, item truncation ( |
|
|
|
Add inspect-rs to your Cargo.toml:
[dependencies]
inspect-rs = "0.1"Derive Inspect on your types and format them:
use inspect_rs::{format_tree, Inspect, InspectCx};
#[derive(Inspect)]
struct Server {
host: String,
port: u16,
running: bool,
#[inspect(secret)]
api_key: String,
}
let server = Server {
host: "127.0.0.1".to_string(),
port: 8080,
running: true,
api_key: "sk_live_99a8b7c6d5e4f3".to_string(),
};
let mut cx = InspectCx::new();
println!("{}", format_tree(&server.inspect(&mut cx)));Server
├── host: "127.0.0.1"
├── port: 8080
├── running: true
└── api_key: [REDACTED]
Tip
When stdout is connected to an interactive terminal, format_tree renders restrained, high-contrast semantic syntax highlighting. When redirected (| less or > log.txt) or when NO_COLOR=1 is set, clean plain text is emitted automatically.
inspect-rs includes a production-grade terminal rendering engine styled like a modern Rust compiler and developer tool—not a demo drenched in random ANSI colors.
Colors communicate structural roles rather than arbitrary text:
| Role | Default Dark Palette | Semantic Purpose |
|---|---|---|
| Type | Warm Copper (#E08A5A) |
Struct names, collection identifiers (Server, Vec) |
| Field | Soft Sky Blue (#6EB4EB) |
Struct field names (host, port, workers) |
| Variant | Soft Lilac (#B491E1) |
Enum variants (Status::Running, Postgres) |
| Key | Teal Cyan (#69CDC3) |
Map keys ("us-east-1", "timeout") |
| String | Sage Green (#8EC680) |
String literals with subdued quotes |
| Number | Warm Gold (#EBC05F) |
Integer and float scalar values (8080, 3.14) |
| Boolean | Soft Magenta (#C387B9) |
Boolean flags (true, false) |
| Punctuation | Subdued Gray (#737882) |
Branch glyphs (├── , └── ), colons, quotes |
| Sensitive | Bold Crimson (#EB5F5F) |
Redacted secrets ([REDACTED]) |
| Truncated | Dimmed Italic Amber (#E1B44B) |
Truncation notices (… 42 more) |
| Error | Bold Red (#F05555) |
Recursive cycles (<cycle>), error variants |
inspect-rs provides 14 classic developer palettes calibrated for dark, light, high-contrast, and legacy terminals:
| Theme | Constructor | CLI / String Name | Aesthetic Profile |
|---|---|---|---|
| Dark (Default) | Theme::dark() |
"dark", "default" |
Warm copper & soft slate for dark terminals |
| Gruvbox | Theme::gruvbox() |
"gruvbox" |
Iconic warm, earthy retro groove palette |
| Nord | Theme::nord() |
"nord" |
Arctic, cool bluish-slate palette |
| Dracula | Theme::dracula() |
"dracula" |
Vibrant dark theme with purple and cyan accents |
| Catppuccin Mocha | Theme::catppuccin_mocha() |
"catppuccin", "catppuccin-mocha" |
Soft pastel comfort palette |
| Tokyo Night | Theme::tokyo_night() |
"tokyo-night" |
Neon dark city aesthetic inspired by Tokyo nightlife |
| One Dark | Theme::one_dark() |
"one-dark", "onedark" |
Iconic Atom and VS Code editor palette |
| Solarized Dark | Theme::solarized_dark() |
"solarized-dark" |
Ethan Schoonover's precision dark palette |
| Solarized Light | Theme::solarized_light() |
"solarized-light" |
Precision Solarized light background palette |
| Light | Theme::light() |
"light" |
Crisp high-contrast palette for light backgrounds |
| Monokai | Theme::monokai() |
"monokai" |
Classic Sublime Text high-contrast palette |
| Ayu Dark | Theme::ayu_dark() |
"ayu-dark", "ayu" |
Warm modern dark editor palette |
| ANSI 16 | Theme::ansi16() |
"ansi16", "ansi" |
Standard 4-bit ANSI for restricted terminals |
| Monochrome | Theme::monochrome() |
"monochrome", "plain" |
Terminal effects (bold, dim, italic, underline) with zero color |
use inspect_rs::{format_tree_with, Theme, TreeConfig};
// 1. Using a typed constructor
let config = TreeConfig::colored().with_theme(Theme::gruvbox());
println!("{}", format_tree_with(&inspected, config));
// 2. Dynamic lookup (case-insensitive, kebab-case or snake_case)
let theme = Theme::by_name("tokyo-night").unwrap_or_default();
let config = TreeConfig::colored().with_theme(theme);
println!("{}", format_tree_with(&inspected, config));use inspect_rs::{AnsiColor, Style, StyleRole, Theme, TreeConfig};
let custom = Theme::dark()
.with(StyleRole::Type, Style::new().bold().fg_ansi(AnsiColor::BrightYellow))
.with(StyleRole::Field, Style::new().fg_ansi(AnsiColor::BrightCyan))
.with(StyleRole::String, Style::new().fg_rgb(140, 230, 140));
let config = TreeConfig::colored().with_theme(custom);inspect-rs adheres to modern Unix CLI standards:
| Signal | Mechanism | Behavior |
|---|---|---|
NO_COLOR |
Environment variable (NO_COLOR=1) |
Completely disables ANSI escapes (https://no-color.org) |
FORCE_COLOR |
Environment variable (FORCE_COLOR=1) |
Forces ANSI color emission even when piped |
TERM=dumb |
Environment variable | Disables styling for dumb/minimal terminals |
| TTY Detection | std::io::IsTerminal |
Disables color when redirecting to files or pipes |
use inspect_rs::{format_tree, format_tree_colored, format_tree_plain, ColorChoice, TreeConfig};
// Automatic detection (TTY + env vars)
let auto = format_tree(&inspected);
// Explicitly uncolored (guaranteed zero ANSI sequences)
let plain = format_tree_plain(&inspected);
// Explicitly colored (forces ANSI escapes)
let colored = format_tree_colored(&inspected);Fine-tune how your structs and fields are exposed:
#[derive(Inspect)]
struct DatabaseConfig {
host: String,
port: u16,
// Secrets are permanently masked in default renderers
#[inspect(secret)]
password: String,
// Sensitive data marked with PII metadata
#[inspect(sensitive)]
admin_email: String,
// Omit field entirely from inspection
#[inspect(skip)]
internal_cache_key: u64,
// Custom display name for the field
#[inspect(rename = "service_url")]
url: String,
}Prevent recursive cycles and inspection bombs from consuming unbounded CPU or memory:
use inspect_rs::{InspectCx, InspectLimits};
let limits = InspectLimits {
max_depth: 12, // Maximum tree nesting depth
max_items: 50, // Maximum elements per collection before truncation
max_nodes: 5000, // Global ceiling on total nodes visited
max_string_length: 512, // String clipping threshold
max_bytes: 2048, // Byte-slice display ceiling
};
let mut cx = InspectCx::with_limits(limits);
let inspected = large_structure.inspect(&mut cx);Recursive structs automatically print <cycle> instead of causing a stack overflow.
inspect-rs is cleanly partitioned into a modular workspace:
┌────────────────────────────────────────────────────────┐
│ Your Application │
│ #[derive(Inspect)] / structs / enums │
└───────────────────────────┬────────────────────────────┘
│ implements Inspect
▼
┌────────────────────────────────────────────────────────┐
│ inspect-core │
│ Inspect trait · ValueRef · InspectCx · Limits · AST │
│ (Zero runtime dependencies) │
└───────────────────────────┬────────────────────────────┘
│ borrows ValueRef
▼
┌────────────────────────────────────────────────────────┐
│ inspect-format │
│ TreeFormatter · Themes · StyleRoles · StyledWriter │
│ (Optional "color" feature via anstyle) │
└───────────────────────────┬────────────────────────────┘
│ re-exports facade
▼
┌────────────────────────────────────────────────────────┐
│ inspect-rs │
│ Convenience helpers: format_tree, Config, API │
└────────────────────────────────────────────────────────┘
| Crate | Responsibility | Dependencies |
|---|---|---|
inspect-core |
Traits, borrowed value graph, context, cycle limits | Zero |
inspect-derive |
Procedural macro #[derive(Inspect)] |
syn, quote, proc-macro2 |
inspect-format |
Tree rendering, themes, styling, stream writing | anstyle (optional) |
inspect-rs |
Top-level facade crate | Workspace members |
inspect-rs is engineered for zero runtime overhead in production:
- Zero Allocations for Scalars: Numbers, booleans, and small values return value copies directly.
- Borrowed Strings: String slices borrow directly without reallocating.
- Zero-Allocation Uncolored Path: When color is disabled,
StyledWriterskips styling logic and streams plain bytes directly to the output. - Direct Stream Writing: Colored rendering emits ANSI sequences directly to the buffer without allocating intermediate strings.
Run the Criterion benchmark suite:
cargo bench| Feature | Debug |
bevy_reflect |
valuable |
inspect-rs |
|---|---|---|---|---|
| Output Type | Opaque String | Dynamic Types / Patching | Visitor Callbacks | Structured Borrowed AST |
| Zero-Copy Scalars | ✕ | ✕ | ✕ | ✓ |
| Lazy Child Traversal | ✕ | ✕ | ✕ | ✓ |
| Secret Redaction | ✕ | ✕ | ✕ | ✓ (#[inspect(secret)]) |
| Resource Limits & Cycles | ✕ | ✕ | ✕ | ✓ |
| Semantic Color Themes | ✕ | ✕ | ✕ | ✓ (14 classic themes) |
| Dependency-Free Core | ✓ | ✕ | ✓ | ✓ (0 dependencies) |
| Safe Rust | ✓ | ✕ | ✓ | ✓ (forbid(unsafe_code)) |
Run any of the built-in examples:
# Demo all 14 color themes sequentially
cargo run --example colored
# Demo a specific theme
cargo run --example colored -- gruvbox
cargo run --example colored -- nord
cargo run --example colored -- dracula
cargo run --example colored -- tokyo-night
# Basic usage
cargo run --example basic
# Struct and enum nested inspection
cargo run --example nested
cargo run --example enums
# Collections (Vec, HashMap, arrays)
cargo run --example collections
# Secrets and sensitive redaction
cargo run --example attributes- Edition: Rust 2024
- Minimum Supported Rust Version:
1.85.0 - Safety:
#![forbid(unsafe_code)]
Dual-licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT License (LICENSE-MIT)
at your option.