rust 58 lines · 8 steps

Applying a diff hunk in Rust

A patch applier walks a hunk's lines against a buffer, verifying context before adding or removing text.

Explained by highlit
1use std::collections::VecDeque;
2 
3#[derive(Debug)]
4pub struct Hunk {
5 pub old_start: usize,
6 pub lines: Vec<HunkLine>,
7}
8 
9#[derive(Debug)]
10pub enum HunkLine {
11 Context(String),
12 Removed(String),
13 Added(String),
14}
15 
16#[derive(Debug)]
17pub enum PatchError {
18 ContextMismatch { line: usize, expected: String, found: String },
19 UnexpectedEof { line: usize },
20}
21 
22pub fn apply_hunk(buffer: &[String], hunk: &Hunk) -> Result<Vec<String>, PatchError> {
23 let mut out: Vec<String> = buffer[..hunk.old_start.saturating_sub(1)].to_vec();
24 let mut cursor = hunk.old_start.saturating_sub(1);
25 let mut pending: VecDeque<&HunkLine> = hunk.lines.iter().collect();
26 
27 while let Some(line) = pending.pop_front() {
28 match line {
29 HunkLine::Context(text) => {
30 let existing = buffer.get(cursor).ok_or(PatchError::UnexpectedEof { line: cursor + 1 })?;
31 if existing != text {
32 return Err(PatchError::ContextMismatch {
33 line: cursor + 1,
34 expected: text.clone(),
35 found: existing.clone(),
36 });
37 }
38 out.push(existing.clone());
39 cursor += 1;
40 }
41 HunkLine::Removed(text) => {
42 let existing = buffer.get(cursor).ok_or(PatchError::UnexpectedEof { line: cursor + 1 })?;
43 if existing != text {
44 return Err(PatchError::ContextMismatch {
45 line: cursor + 1,
46 expected: text.clone(),
47 found: existing.clone(),
48 });
49 }
50 cursor += 1;
51 }
52 HunkLine::Added(text) => out.push(text.clone()),
53 }
54 }
55 
56 out.extend_from_slice(&buffer[cursor..]);
57 Ok(out)
58}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Modeling each diff line as an enum variant lets one match express context, removal, and addition cleanly.
  2. 2Verifying context and removed lines against the buffer catches stale patches before corrupting output.
  3. 3Returning a rich error enum lets callers report exactly which line mismatched and why.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Applying a diff hunk in Rust — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code