# Documentation Generation Guide
## Recommended Approach: Separate Binary with Auto-Registration
The recommended pattern uses a separate binary with automatic diagnostic registration. This keeps your production binary clean while enabling effortless doc generation.
### Complete Example
**Step 1: Define Errors with Auto-Registration**
```rust
// src/errors/mod.rs
use waddling_errors_macros::{component, primary, diag};
component! { Auth { value: "AUTH", docs: "Authentication system" } }
primary! { Token { value: "TOKEN", docs: "Token-related errors" } }
diag! {
<json, html>, // Auto-register when types load
E.AUTH.TOKEN.EXPIRED: {
message: "JWT token expired at {{timestamp}}",
fields: [timestamp],
'CR 'Pub description: "Your session has expired. Please log in again.",
'CR 'Dev description: "Token TTL exceeded. Check refresh logic.",
'CR 'Int hints: ["Query auth_tokens table", "Check Redis TTL config"],
'R role: "Public",
'R tags: ["auth", "security"],
},
}
```
**Step 2: Create Doc Generation Binary**
```rust
// src/bin/doc_gen.rs
use my_app::errors::*; // Loads error types → triggers auto-registration
use waddling_errors::doc_generator::{DocRegistry, HtmlRenderer, JsonRenderer};
use waddling_errors::registry;
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("📚 Generating error documentation...\n");
let mut doc_registry = DocRegistry::new(
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_VERSION")
);
// Transfer auto-registered diagnostics from global registry
registry::register_all_with_doc_gen(&mut doc_registry);
// Show statistics
let (diagnostics_count, components_count, primaries_count, _, _) = registry::statistics();
println!("✓ {} diagnostics registered", diagnostics_count);
println!("✓ {} components registered", components_count);
println!("✓ {} primaries registered\n", primaries_count);
// Generate documentation for all roles
doc_registry.render_all_roles(
vec![Box::new(HtmlRenderer::new()), Box::new(JsonRenderer)],
"target/docs"
)?;
println!("✅ Documentation generated successfully:");
println!(" 📘 target/docs/{}-pub.html", env!("CARGO_PKG_NAME"));
println!(" 👨💻 target/docs/{}-dev.html", env!("CARGO_PKG_NAME"));
println!(" 🔒 target/docs/{}-int.html", env!("CARGO_PKG_NAME"));
Ok(())
}
```
**Step 3: Configure Cargo.toml**
```toml
[dependencies]
waddling-errors = { version = "0.7", features = ["metadata"] }
waddling-errors-macros = { version = "0.7", features = ["metadata", "auto-register"] }
[dev-dependencies]
waddling-errors = { version = "0.7", features = ["doc-gen"] }
[[bin]]
name = "doc-gen"
path = "src/bin/doc_gen.rs"
required-features = ["waddling-errors/doc-gen", "waddling-errors-macros/auto-register"]
```
**Step 4: Generate Documentation**
```bash
cargo run --bin doc-gen --features waddling-errors/doc-gen,waddling-errors-macros/auto-register
```
**Or create a cargo alias** in `.cargo/config.toml`:
```toml
[alias]
docs = "run --bin doc-gen --features waddling-errors/doc-gen,waddling-errors-macros/auto-register"
```
Then simply run:
```bash
cargo docs
```
### Why This Pattern?
**✅ Advantages:**
1. **Clean Production Binary**
- No serde/serde_json dependencies
- No HTML/JSON renderers
- No documentation strings
- Only lightweight error structs
2. **Zero Boilerplate**
- `<json, html>` in `diag!` handles all registration
- No manual `register_code()` calls
- No registration function to maintain
3. **Separate Concerns**
- Doc generation isolated from app logic
- No error imports polluting main.rs
- Clean separation like benchmarks/tests
4. **Idiomatic Rust**
- Same pattern as `criterion` benchmarks
- Follows cargo's `[[bin]]` conventions
- Feature-gated development tools
5. **Fast Iteration**
- Errors load → auto-register → generate docs
- Single command regenerates everything
- No intermediate steps
---
## Alternative Approaches
### Method 1: Build Script (Automated)
For automatic doc generation during compilation (not recommended - slows builds):
```rust
// build.rs
#[cfg(feature = "doc-gen")]
fn main() {
use waddling_errors::doc_generator::{DocRegistry, HtmlRenderer, JsonRenderer};
use waddling_errors::registry;
// Note: This only works if errors are already defined and loaded
let mut doc_registry = DocRegistry::new(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
// You still need to trigger loading somehow
// This is why separate binary is better
registry::register_all_with_doc_gen(&mut doc_registry);
doc_registry.render_all_roles(vec![
Box::new(HtmlRenderer::new()),
Box::new(JsonRenderer),
], "docs/errors").expect("Failed to generate documentation");
println!("cargo:rerun-if-changed=src/errors/");
}
#[cfg(not(feature = "doc-gen"))]
fn main() {}
```
**Downsides:**
- Slows down every build
- Hard to trigger error loading
- Couples doc-gen to compilation
### Method 2: Manual Registration (Legacy)
Before auto-registration existed, you had to manually register each error:
```rust
// src/bin/generate_docs.rs
use my_project::errors;
use waddling_errors::doc_generator::{DocRegistry, HtmlRenderer, JsonRenderer, HtmlCustomization};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut registry = DocRegistry::new(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
// ❌ Manual registration - tedious and error-prone
registry.register_component("AUTH", Some("Authentication"), &[], &["security"])?;
registry.register_code_extended(
&errors::E_AUTH_TOKEN_MISSING,
"Token missing",
&["Include Authorization header"],
&["auth"],
Some(Role::Public),
&[], None, &[],
)?;
// ... repeat for every error
// Optional: Add branding
let custom = HtmlCustomization::new()
.with_logo("https://example.com/logo.svg", "MyProject")
.with_accent_color("#FF5722", "#FF8A65");
let renderer = HtmlRenderer::with_customization(custom);
registry.render_all_roles(vec![
Box::new(renderer),
Box::new(JsonRenderer),
], "target/docs")?;
println!("✅ Documentation generated in target/docs/");
Ok(())
}
```
**Downsides:**
- Lots of boilerplate
- Easy to forget errors
- Maintenance burden
**Use this only if:**
- Not using macros
- Need maximum control over registration
- Legacy codebase migration
### Method 3: Cargo Alias (Convenience)
Create `.cargo/config.toml`:
```toml
[alias]
docs = "run --bin doc-gen --features waddling-errors/doc-gen,waddling-errors-macros/auto-register"
gendocs = "docs" # alias for alias
```
Then use:
```bash
cargo docs
```
### Future: CLI Tool (Planned)
A dedicated CLI tool is planned for a future release:
```bash
# Install (future)
cargo install waddling-errors-cli
# Generate docs (future)
cargo wde generate-docs --format html,json --output docs/
# Validate (future)
cargo wde validate --check-links
# Serve (future)
cargo wde serve --port 8080
```
**Status:** CLI will be added in a future PR as a separate workspace crate.
---
## CI/CD Integration
- Track usage across codebase
- Generate docs on build
## CI/CD Integration
### GitHub Actions
```yaml
# .github/workflows/docs.yml
name: Generate Documentation
on:
push:
branches: [main]
pull_request:
jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Generate error documentation
run: cargo run --bin doc-gen --features waddling-errors/doc-gen,waddling-errors-macros/auto-register
- name: Deploy to GitHub Pages
if: github.ref == 'refs/heads/main'
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./target/docs
```
### GitLab CI
```yaml
# .gitlab-ci.yml
generate-docs:
stage: docs
image: rust:latest
script:
- cargo run --bin doc-gen --features waddling-errors/doc-gen,waddling-errors-macros/auto-register
artifacts:
paths:
- target/docs
only:
- main
```
### Pre-commit Hook
```bash
#!/bin/bash
# .git/hooks/pre-commit
echo "Regenerating error documentation..."
cargo run --bin doc-gen --features waddling-errors/doc-gen,waddling-errors-macros/auto-register --quiet
git add target/docs/
```
---
### Current Capabilities
We can enrich error instances at runtime with:
```rust
use waddling_errors::prelude::*;
// At error site
let err = ERR_TOKEN_MISSING
.with_field("token_name", "refresh_token")
.with_field("user_id", user_id)
.with_field("request_id", req.id())
.with_timestamp() // Add when error occurred
.with_context("endpoint", "/api/auth/refresh");
```
### Proposed Runtime Enrichment
```rust
pub struct RuntimeErrorInfo {
// Existing
pub code: String,
pub fields: HashMap<String, String>,
// NEW: Runtime context
pub timestamp: Option<SystemTime>,
pub thread_id: Option<String>,
pub process_id: Option<u32>,
pub stack_trace: Option<Vec<String>>, // If backtrace feature enabled
// NEW: Performance data
pub allocation_count: Option<usize>, // Memory allocations at error time
pub heap_size: Option<usize>, // Current heap usage
pub duration_since_start: Option<Duration>, // App uptime
// NEW: Correlation
pub correlation_id: Option<String>, // For distributed tracing
pub parent_span_id: Option<String>, // OpenTelemetry integration
pub causation_chain: Vec<String>, // Previous errors that led here
}
```
### Example: Comprehensive Error Tracking
```rust
#[derive(Debug)]
pub struct EnrichedError {
pub code: Code<Component, Primary>,
// Compile-time (embedded in binary)
pub source_file: &'static str,
pub source_line: u32,
pub defined_in_version: &'static str,
// Runtime (captured when error occurs)
pub timestamp: SystemTime,
pub stack_trace: Option<Vec<String>>,
pub fields: HashMap<String, String>,
// Performance metrics
pub memory_usage_mb: f64,
pub cpu_time_ms: u64,
// Distributed tracing
pub trace_id: Option<String>,
pub span_id: Option<String>,
}
impl EnrichedError {
pub fn capture(code: Code<Component, Primary>) -> Self {
Self {
code,
source_file: file!(),
source_line: line!(),
defined_in_version: env!("CARGO_PKG_VERSION"),
timestamp: SystemTime::now(),
stack_trace: capture_backtrace(),
fields: HashMap::new(),
memory_usage_mb: get_memory_usage(),
cpu_time_ms: get_cpu_time(),
trace_id: get_trace_id(),
span_id: get_span_id(),
}
}
}
```
## 6. Proposed CLI Tool
```bash
# Install
cargo install waddling-cli
# Generate docs
waddling docs generate --format html,json --output docs/
# Analyze codebase
waddling analyze --show-usage --find-duplicates
# Validate
waddling validate --check-links --check-codes
# Interactive explorer
waddling serve --port 8080
```
## Integration with Development Workflow
### 1. Pre-commit Hook
```bash
#!/bin/bash
# .git/hooks/pre-commit
git add docs/
```
### 2. CI/CD Pipeline
```yaml
# .github/workflows/docs.yml
- name: Generate Error Documentation
run: cargo run --bin generate_docs --features doc-gen
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./target/docs
```
### 3. IDE Integration
VSCode extension that:
- Shows error documentation on hover
- Autocompletes error codes
- Highlights deprecated errors
- Links to online docs
## Summary
**Compile-Time Enrichment:**
- ✅ Source locations (file:line:col)
- ✅ Git metadata (commit, branch)
- ✅ Build context (Rust version, features)
- 🔄 Usage analysis (with proc macro support)
**Runtime Enrichment:**
- ✅ Timestamps, thread IDs
- ✅ Stack traces (with backtrace)
- ✅ User-provided context fields
- 🔄 Performance metrics
- 🔄 Distributed tracing integration
**Developer Experience:**
- Build scripts for automatic generation
- Cargo aliases for easy commands
- CI/CD integration examples
- Future: CLI tool for all operations