lupa 0.1.0

Interactive object inspector for Rust — web UI + TUI + snapshot diffing
Documentation
//! Line‑by‑line diff computation between two snapshots.
//!
//! This module uses the [`similar`](https://crates.io/crates/similar) crate
//! to compute a Myers diff of the debug representations. The result is a
//! sequence of `DiffChunk`s that can be displayed in the web UI or the TUI.

use similar::{ChangeTag, TextDiff};
use crate::state::Snapshot;
use serde::{Deserialize, Serialize};

/// A single chunk of a diff – a contiguous block of lines that are either
/// equal, inserted or deleted.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiffChunk {
    /// Whether this chunk is equal, an insertion or a deletion.
    pub tag: DiffTag,
    /// The actual content (one or more lines) of this chunk.
    pub content: String,
}

/// Classification of a diff chunk.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DiffTag {
    /// Lines that are present in both snapshots.
    Equal,
    /// Lines that appear only in the new snapshot.
    Insert,
    /// Lines that appear only in the old snapshot.
    Delete,
}

/// Compute a line‑level diff between the debug representations of two
/// snapshots.
///
/// # Example
/// ```
/// # use lupa::Snapshot;
/// # let old = Snapshot::capture("old", "Hello\nWorld".into(), "file.rs", 1);
/// # let new = Snapshot::capture("new", "Hello\nRust".into(), "file.rs", 2);
/// let chunks = lupa::diff::diff_snapshots(&old, &new);
/// assert_eq!(chunks.len(), 3); // "Hello" equal, "World" deleted, "Rust" inserted
/// ```
pub fn diff_snapshots(old: &Snapshot, new: &Snapshot) -> Vec<DiffChunk> {
    let text_diff = TextDiff::from_lines(&old.debug_repr, &new.debug_repr);

    text_diff
        .iter_all_changes()
        .map(|change| {
            let tag = match change.tag() {
                ChangeTag::Equal  => DiffTag::Equal,
                ChangeTag::Insert => DiffTag::Insert,
                ChangeTag::Delete => DiffTag::Delete,
            };
            DiffChunk {
                tag,
                content: change.value().to_string(),
            }
        })
        .collect()
}