diffo 0.2.0

Semantic diffing for Rust structs via serde
Documentation
//! Demonstrates applying diffs to produce new values.

use diffo::{apply, diff};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct User {
    name: String,
    email: String,
    age: u32,
}

#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Config {
    version: String,
    settings: Settings,
}

#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Settings {
    timeout: u32,
    retries: u32,
}

fn main() {
    println!("=== Diff Application Demo ===\n");

    // 1. Simple field update
    println!("1. Simple Field Update");
    let user_v1 = User {
        name: "Alice".into(),
        email: "alice@example.com".into(),
        age: 30,
    };

    let user_v2 = User {
        name: "Alice".into(),
        email: "alice@example.com".into(),
        age: 31,
    };

    let diff_1_2 = diff(&user_v1, &user_v2).unwrap();
    println!("   Changes: {}", diff_1_2.len());

    let result: User = apply(&user_v1, &diff_1_2).unwrap();
    println!("   Applied diff: age {} -> {}", user_v1.age, result.age);
    assert_eq!(result, user_v2);
    println!("   ✓ Result matches expected\n");

    // 2. Multiple field changes
    println!("2. Multiple Field Changes");
    let user_v3 = User {
        name: "Alicia".into(),
        email: "alicia@newdomain.com".into(),
        age: 31,
    };

    let diff_2_3 = diff(&user_v2, &user_v3).unwrap();
    println!("   Changes: {}", diff_2_3.len());

    let result: User = apply(&user_v2, &diff_2_3).unwrap();
    println!("   Applied diff:");
    println!("     name: {} -> {}", user_v2.name, result.name);
    println!("     email: {} -> {}", user_v2.email, result.email);
    assert_eq!(result, user_v3);
    println!("   ✓ Result matches expected\n");

    // 3. Nested structure changes
    println!("3. Nested Structure Changes");
    let config_v1 = Config {
        version: "1.0".into(),
        settings: Settings {
            timeout: 30,
            retries: 3,
        },
    };

    let config_v2 = Config {
        version: "1.1".into(),
        settings: Settings {
            timeout: 60,
            retries: 5,
        },
    };

    let diff_config = diff(&config_v1, &config_v2).unwrap();
    println!("   Changes: {}", diff_config.len());

    let result: Config = apply(&config_v1, &diff_config).unwrap();
    println!("   Applied diff:");
    println!("     version: {} -> {}", config_v1.version, result.version);
    println!(
        "     settings.timeout: {} -> {}",
        config_v1.settings.timeout, result.settings.timeout
    );
    println!(
        "     settings.retries: {} -> {}",
        config_v1.settings.retries, result.settings.retries
    );
    assert_eq!(result, config_v2);
    println!("   ✓ Result matches expected\n");

    // 4. Array modifications
    println!("4. Array Modifications");
    let numbers_v1 = vec![1, 2, 3];
    let numbers_v2 = vec![1, 2, 3, 4, 5];

    let diff_numbers = diff(&numbers_v1, &numbers_v2).unwrap();
    println!("   Changes: {}", diff_numbers.len());

    let result: Vec<i32> = apply(&numbers_v1, &diff_numbers).unwrap();
    println!("   Applied diff: {:?} -> {:?}", numbers_v1, result);
    assert_eq!(result, numbers_v2);
    println!("   ✓ Result matches expected\n");

    // 5. Roundtrip demonstration
    println!("5. Roundtrip: apply(base, diff(base, target)) = target");
    let original = User {
        name: "Bob".into(),
        email: "bob@example.com".into(),
        age: 25,
    };

    let modified = User {
        name: "Robert".into(),
        email: "robert@newdomain.com".into(),
        age: 26,
    };

    let d = diff(&original, &modified).unwrap();
    let reconstructed: User = apply(&original, &d).unwrap();

    println!("   Original:      {:?}", original);
    println!("   Modified:      {:?}", modified);
    println!("   Reconstructed: {:?}", reconstructed);
    assert_eq!(reconstructed, modified);
    println!("   ✓ Roundtrip successful!\n");

    // 6. Undo operation
    println!("6. Undo Operation (reverse diff)");
    let current = User {
        name: "Charlie".into(),
        email: "charlie@example.com".into(),
        age: 35,
    };

    let updated = User {
        name: "Charles".into(),
        email: "charles@example.com".into(),
        age: 36,
    };

    // Forward diff: current -> updated
    let forward_diff = diff(&current, &updated).unwrap();
    let after_update: User = apply(&current, &forward_diff).unwrap();
    println!("   After update: {:?}", after_update);
    assert_eq!(after_update, updated);

    // Reverse diff: updated -> current (undo)
    let reverse_diff = diff(&updated, &current).unwrap();
    let after_undo: User = apply(&after_update, &reverse_diff).unwrap();
    println!("   After undo:   {:?}", after_undo);
    assert_eq!(after_undo, current);
    println!("   ✓ Undo successful!\n");

    println!("=== Summary ===");
    println!("Diff application enables:");
    println!("  - State reconstruction from base + diff");
    println!("  - Undo/redo operations");
    println!("  - Version control for data structures");
    println!("  - Incremental updates without full replacement");
}