ferrotype 0.1.1

An opinionated wrapper for insta.rs
Documentation
# Ferrotype

> A tintype, also known as a melainotype or ferrotype, is a photograph made by creating a direct positive on a thin sheet of metal, colloquially called 'tin', coated with a dark lacquer or enamel and used as the support for the photographic emulsion.

Ferrotype is an opinionated wrapper around [insta.rs](https://insta.rs) that makes it easy to create and maintain structured snapshot tests with multiple named sections.

## Features

- **Multi-section snapshots** with automatically formatted titles
- **Rich content support** for various data types:
  - Debug output (`add_debug`)
  - Token streams (`add_token_stream`) with pretty formatting
  - [Bluegum]https://docs.rs/bluegum tree structures
  - Hex dumps of binary data with `hex` crate
  - ANSI color code stripping
- **Deterministic output** with automatic filtering of memory addresses and non-deterministic data
- **Flexible organization** with configurable snapshot directory structure
- **Debugging support** with backtraces and error handling
- **Seamless integration** with existing insta.rs workflows

## Installation

Add this to your `Cargo.toml`:

```toml
[dev-dependencies]
ferrotype = "0.1"
```

## Quick Start

Here's the minimum required code to create a ferrotype snapshot:

```rust
use ferrotype::Ferrotype;

// 1. Create a new snapshot
let mut snapshot = Ferrotype::new();

// 2. Add content to sections
snapshot.add("Input", "Hello, world!".to_string());

// 3. Assert the snapshot
ferrotype::assert!(snapshot);
```

## Comprehensive Example

```rust
use ferrotype::Ferrotype;

#[derive(Debug)]
struct TestData {
    name: String,
    count: u32,
    items: Vec<String>,
}

let mut snapshot = Ferrotype::new();

// Configure snapshot behavior
snapshot.set_filter_memory_addresses(true);  // Default: true
snapshot.set_expect_errors(false);           // Default: false

// Add different types of content
snapshot.add("Description", "Testing my awesome feature".to_string());

let test_data = TestData {
    name: "test".to_string(),
    count: 42,
    items: vec!["a".to_string(), "b".to_string()],
};
snapshot.add_debug("Test Data", &test_data);

// Add hex dump of binary data
#[cfg(feature = "hex")]
snapshot.add_hex("Binary Data", &[0xDE, 0xAD, 0xBE, 0xEF]);

// Add formatted Rust code
#[cfg(feature = "tokenstream")]
{
    let tokens: proc_macro2::TokenStream =
        "fn hello() { println!(\"Hello!\"); }".parse().unwrap();
    snapshot.add_token_stream("Generated Code", &tokens);
}

// Assert the snapshot
ferrotype::assert!(snapshot);
```

## Examples

The crate includes several example programs demonstrating different aspects of ferrotype:

- **`basic`** - Core functionality and simple usage patterns
- **`filtering`** - Demonstrates all filtering options for non-deterministic data
- **`features`** - Shows feature-specific functionality (hex dumps, token streams, etc.)
- **`testing`** - Real-world testing scenarios with an API client

Run examples with:

```bash
# Basic example
cargo run --example basic

# Filtering demonstration
cargo run --example filtering

# All features (requires all features enabled)
cargo run --example features --all-features

# Testing example
cargo run --example testing
```

## Snapshot Output Format

The above example would create a snapshot file like this:

```yaml
---
source: src/lib.rs
expression: "use my_crate::comprehensive_example"
---
Description: >
  Testing my awesome feature

TestData: >
  TestData {
      name: "test",
      count: 42,
      items: [
          "a",
          "b",
      ],
  }

BinaryData: >
  Length: 4 (0x4) bytes
  0000:   de ad be ef                                       ....

GeneratedCode: >
  fn hello() {
      println!("Hello!");
  }

Backtrace: >
  0: my_crate::comprehensive_example
             at src/lib.rs:42:5
  1: my_crate::comprehensive_example::{{closure}}
             at src/lib.rs:41:1
  ...
```

## Available Methods

### Core Methods

- `Ferrotype::new()` - Create a new snapshot builder
- `add(title, content)` - Add a section with string content
- `add_debug(title, item)` - Add a section with debug-formatted content
- `as_string()` - Get the formatted snapshot content
- `print()` - Print to stdout

### Configuration

- `set_expect_errors(bool)` - Configure whether errors are expected
- `set_filter_memory_addresses(bool)` - Toggle memory address filtering
- `add_backtrace()` - Add current call stack to snapshot

### Feature-specific Methods

These methods are available when the corresponding features are enabled:

#### `bluegum` feature

- `add_bluegum(title, tree)` - Add a rendered bluegum tree
- `add_bluegum_builder(title, builder)` - Add a pre-built bluegum structure
- `add_bluegum_builder_with(title, closure)` - Build bluegum structure with closure
- `add_bluegum_with(title, tree, styles)` - Add bluegum tree with custom styles

#### `tokenstream` feature

- `add_token_stream(title, tokens)` - Add formatted Rust code from TokenStream

#### `anstream` feature

- `add_strip_str(title, content)` - Add content with ANSI codes stripped

#### `hex` feature

- `add_hex(title, bytes)` - Add hex dump of binary data

## Configuration Options

### Snapshot Directory

Control where snapshots are stored:

```rust,ignore
// Use custom folder structure
#[use_folder(parser, tests)]
ferrotype::assert!(snapshot);

// Results in snapshots stored in: snapshots/parser/tests/
```

### Feature Flags

```toml
[dev-dependencies]
ferrotype = { version = "0.1", features = ["hex", "bluegum"] }

# Or disable default features and pick specific ones
ferrotype = { version = "0.1", default-features = false, features = ["tokenstream"] }
```

Available features:

- `dot_snapshots` - Store snapshots in `.snapshots/` instead of `snapshots/`
- `tokenstream` - Enable `add_token_stream()` for formatting Rust code
- `bluegum` - Enable bluegum tree rendering methods
- `anstream` - Enable `add_strip_str()` for ANSI code removal
- `hex` - Enable `add_hex()` for binary data hex dumps

All features are enabled by default.

### Error Handling

```rust,no_run
use ferrotype::Ferrotype;

let mut snapshot = Ferrotype::new();
snapshot.set_expect_errors(true);  // Don't fail test on errors
snapshot.add("Error Case", "Some error output".to_string());
ferrotype::assert!(snapshot);
```

### Memory Address Filtering

```rust,no_run
use ferrotype::Ferrotype;

#[derive(Debug)]
struct SomeStruct {
    value: i32,
}

let some_struct = SomeStruct { value: 42 };
let mut snapshot = Ferrotype::new();
snapshot.set_filter_memory_addresses(false);  // Preserve memory addresses
snapshot.add_debug("Raw Debug", &some_struct);
ferrotype::assert!(snapshot);
```

## Integration with Existing Tests

Ferrotype works alongside existing insta.rs tests:

```rust,no_run
use ferrotype::Ferrotype;

// Existing insta test
fn existing_test() {
    let my_data = vec![1, 2, 3];
    insta::assert_debug_snapshot!(my_data);
}

// New ferrotype test
fn enhanced_test() {
    let input_data = "test input";
    let output_data = vec!["result1", "result2"];
    let analysis_results = "analysis complete";

    let mut snapshot = Ferrotype::new();
    snapshot.add("Input", input_data.to_string());
    snapshot.add_debug("Output", &output_data);
    snapshot.add("Analysis", analysis_results.to_string());
    ferrotype::assert!(snapshot);
}
```

## Best Practices

1. **Use descriptive section titles**: They become the headings in your snapshots
2. **Group related data**: Put related information in the same test
3. **Enable memory filtering**: Keeps snapshots deterministic across runs
4. **Use appropriate content methods**: `add_debug` for structs, `add_hex` for binary data, etc.
5. **Review snapshots**: Use `cargo insta review` to accept/reject changes

## License

Licensed under the Blue Oak Model License 1.0.0 - see the [LICENSE](LICENSE) file for details.