<div align="center">
# inspect-rs
**Universal introspection and semantic terminal rendering for Rust.**
[](https://crates.io/crates/inspect-rs)
[](https://docs.rs/inspect-rs)
[-fbbf24.svg?style=flat-square&labelColor=000000&logo=rust&logoColor=white)](https://blog.rust-lang.org/2025/02/20/Rust-1.85.0.html)
[](https://github.com/programmersd21/inspect-rs)
[](https://github.com/programmersd21/inspect-rs)
[](#license)
<br />
<img src="assets/demo.png" alt="inspect-rs semantic terminal inspection demo" width="840" />
Inspect Rust values as traversable structured data without coupling your application<br />to a debugger, serializer, UI framework, or runtime reflection engine.
</div>
---
## Highlights
<table>
<tr>
<td width="50%">
### ✦ Zero-Copy & Lazy Traversal
Scalar values and strings borrow directly from target memory without eager allocations. Child structures evaluate on-demand via lightweight closures.
</td>
<td width="50%">
### ✦ Production Terminal Styling
Semantic syntax highlighting communicating structure—types, fields, enums, variants, and secrets. Automatic TTY, `NO_COLOR`, and `FORCE_COLOR` handling.
</td>
</tr>
<tr>
<td width="50%">
### ✦ Built-In Security Model
First-class support for `#[inspect(secret)]` (redacted to `[REDACTED]`) and `#[inspect(sensitive)]` PII classification. Secrets never leak into default logs.
</td>
<td width="50%">
### ✦ Memory & Resource Safety
Configurable limits for recursion depth, item truncation (`… N more`), string bounds, and cycle detection to neutralize traversal bombs.
</td>
</tr>
<tr>
<td width="50%">
### ✦ Strict Decoupling
`inspect-core` is 100% dependency-free with zero knowledge of terminal escapes. Styling lives strictly in `inspect-format`.
</td>
<td width="50%">
### ✦ 100% Safe Rust
`#![forbid(unsafe_code)]` enforced workspace-wide across every crate, macro, example, and test suite.
</td>
</tr>
</table>
---
## Quick Start
Add `inspect-rs` to your `Cargo.toml`:
```toml
[dependencies]
inspect-rs = "0.1"
```
Derive `Inspect` on your types and format them:
```rust
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)));
```
### Output
```text
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.
---
## Beautiful Terminal Output
`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.
### Semantic Color Roles
Colors communicate structural roles rather than arbitrary text:
| **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 |
---
## Classic Theme Suite
`inspect-rs` provides 14 classic developer palettes calibrated for dark, light, high-contrast, and legacy terminals:
| **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 |
### Selecting Themes
```rust
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));
```
### Custom Themes via Builder
```rust
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);
```
---
## Controlling Colors & Terminal Modes
`inspect-rs` adheres to modern Unix CLI standards:
| **`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 |
```rust
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);
```
---
## Derive Macro Attributes
Fine-tune how your structs and fields are exposed:
```rust
#[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,
}
```
---
## Safety & Resource Limits
Prevent recursive cycles and inspection bombs from consuming unbounded CPU or memory:
```rust
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.
---
## Architecture
`inspect-rs` is cleanly partitioned into a modular workspace:
```text
┌────────────────────────────────────────────────────────┐
│ 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 │
└────────────────────────────────────────────────────────┘
```
| [`inspect-core`](crates/inspect-core) | Traits, borrowed value graph, context, cycle limits | **Zero** |
| [`inspect-derive`](crates/inspect-derive) | Procedural macro `#[derive(Inspect)]` | `syn`, `quote`, `proc-macro2` |
| [`inspect-format`](crates/inspect-format) | Tree rendering, themes, styling, stream writing | `anstyle` (optional) |
| [`inspect-rs`](crates/inspect) | Top-level facade crate | Workspace members |
---
## Performance
`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, `StyledWriter` skips 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:
```bash
cargo bench
```
---
## Comparison
| **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)`)** |
---
## Examples
Run any of the built-in examples:
```bash
# 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
```
---
## MSRV & Edition
- **Edition**: Rust 2024
- **Minimum Supported Rust Version**: `1.85.0`
- **Safety**: `#![forbid(unsafe_code)]`
---
## License
Dual-licensed under either of:
- **Apache License, Version 2.0** ([LICENSE-APACHE](LICENSE-APACHE))
- **MIT License** ([LICENSE-MIT](LICENSE-MIT))
at your option.