weavatrix-edit 0.1.7

Deterministic, Unicode-safe edit-plan validation and application for source refactoring
Documentation
use std::io::{self, Write};

use weavatrix_edit::{ByteEdit, Provenance, prepare_byte_edits};

const PROVEN: &str = Provenance::EXACT_LSP;

#[test]
fn prepared_edits_stream_the_same_bytes_as_in_memory_application() {
    let source = "alpha beta gamma";
    let edits = [
        ByteEdit::replace(6..10, "beta", "B", PROVEN),
        ByteEdit::insert(0, "<", PROVEN),
        ByteEdit::insert(source.len(), ">", PROVEN),
    ];
    let prepared = prepare_byte_edits(source, &edits).unwrap();
    let expected = prepared.apply();
    let mut output = Vec::new();

    let summary = prepared.write_to(&mut output).unwrap();

    assert_eq!(output, expected.text.as_bytes());
    assert_eq!(summary.edits_applied, expected.edits_applied);
    assert_eq!(summary.bytes_before, expected.bytes_before);
    assert_eq!(summary.bytes_written, expected.bytes_after);
}

#[test]
fn chunks_preserve_insert_order_and_skip_empty_segments() {
    let source = "abc";
    let edits = [
        ByteEdit::insert(0, "<", PROVEN),
        ByteEdit::insert(0, "[", PROVEN),
        ByteEdit::delete(1..2, "b", PROVEN),
        ByteEdit::insert(3, ">", PROVEN),
    ];
    let prepared = prepare_byte_edits(source, &edits).unwrap();
    let chunks = prepared.chunks().collect::<Vec<_>>();

    assert_eq!(chunks, ["<", "[", "a", "c", ">"]);
    assert_eq!(chunks.concat(), prepared.apply().text);
}

#[test]
fn writer_failures_are_returned_without_panicking() {
    let source = "alpha beta";
    let prepared =
        prepare_byte_edits(source, &[ByteEdit::replace(6..10, "beta", "gamma", PROVEN)]).unwrap();
    let mut writer = FailingWriter {
        remaining: 7,
        bytes: Vec::new(),
    };

    let error = prepared.write_to(&mut writer).unwrap_err();

    assert_eq!(error.kind(), io::ErrorKind::BrokenPipe);
    assert_eq!(writer.bytes, b"alpha g");
}

struct FailingWriter {
    remaining: usize,
    bytes: Vec<u8>,
}

impl Write for FailingWriter {
    fn write(&mut self, buffer: &[u8]) -> io::Result<usize> {
        if self.remaining == 0 {
            return Err(io::Error::new(io::ErrorKind::BrokenPipe, "closed"));
        }
        let written = self.remaining.min(buffer.len());
        self.bytes.extend_from_slice(&buffer[..written]);
        self.remaining -= written;
        Ok(written)
    }

    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}