pub struct Patch {
pub file_path: PathBuf,
pub hunks: Vec<Hunk>,
pub ends_with_newline: bool,
}Expand description
Represents all the changes to be applied to a single file.
A Patch contains a target file path and a list of Hunks. It is typically
created by parsing a diff string (Unified Diff, Markdown block, or Conflict Markers)
using functions like parse_auto() or parse_diffs(). It can represent
file modifications, creations, or deletions.
It is the primary unit of work for the apply functions.
§Example
let diff = r#"
```diff
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,3 @@
fn main() {
- println!("Hello, world!");
+ println!("Hello, mpatch!");
}
```
"#;
let patch = parse_single_patch(diff).unwrap();
assert_eq!(patch.file_path.to_str(), Some("src/main.rs"));
assert_eq!(patch.hunks.len(), 1);
assert!(patch.ends_with_newline);Fields§
§file_path: PathBufThe relative path of the file to be patched, from the target directory.
This path is extracted from the --- a/path/to/file header in the diff.
It’s a PathBuf, so you can use it directly with filesystem operations
or convert it to a string for display.
§Example
let patch = parse_single_patch(diff).unwrap();
assert_eq!(patch.file_path.to_str(), Some("src/main.rs"));
println!("Patch targets the file: {}", patch.file_path.display());hunks: Vec<Hunk>A list of hunks to be applied to the file.
ends_with_newline: boolIndicates whether the file should end with a newline.
This is determined by the presence of \ No newline at end of file
in the diff.
Implementations§
Source§impl Patch
impl Patch
Sourcepub fn from_texts(
file_path: impl Into<PathBuf>,
old_text: &str,
new_text: &str,
context_len: usize,
) -> Result<Self, ParseError>
pub fn from_texts( file_path: impl Into<PathBuf>, old_text: &str, new_text: &str, context_len: usize, ) -> Result<Self, ParseError>
Creates a new Patch by comparing two texts.
This function generates a unified diff between the old_text and new_text
and then parses it into a Patch object. This allows mpatch to be used
not just for applying patches, but also for creating them.
§Arguments
file_path- The path to associate with the patch (e.g.,src/main.rs).old_text- The original text content.new_text- The new, modified text content.context_len- The number of context lines to include around changes.
§Example
let old_code = "fn main() {\n println!(\"old\");\n}\n";
let new_code = "fn main() {\n println!(\"new\");\n}\n";
let patch = Patch::from_texts("src/main.rs", old_code, new_code, 3).unwrap();
assert_eq!(patch.file_path.to_str(), Some("src/main.rs"));
assert_eq!(patch.hunks.len(), 1);
assert_eq!(patch.hunks[0].removed_lines(), vec![" println!(\"old\");"]);
assert_eq!(patch.hunks[0].added_lines(), vec![" println!(\"new\");"]);Sourcepub fn invert(&self) -> Patch
pub fn invert(&self) -> Patch
Creates a new Patch that reverses the changes in this one.
Each hunk in the patch is inverted, swapping additions and deletions. This is useful for “un-applying” a patch.
Note: The ends_with_newline status of the reversed patch is ambiguous
in the unified diff format, so it defaults to true.
§Example
let patch = Patch {
file_path: "file.txt".into(),
hunks: vec![Hunk {
lines: vec![
" context".to_string(),
"-deleted".to_string(),
"+added".to_string(),
],
old_start_line: Some(10),
new_start_line: Some(10),
}],
ends_with_newline: true,
};
let inverted = patch.invert();
let inverted_hunk = &inverted.hunks[0];
assert_eq!(inverted_hunk.removed_lines(), vec!["added"]);
assert_eq!(inverted_hunk.added_lines(), vec!["deleted"]);Sourcepub fn is_creation(&self) -> bool
pub fn is_creation(&self) -> bool
Checks if the patch represents a file creation.
A patch is considered a creation if its first hunk is an addition-only hunk that applies to an empty file (i.e., its “match block” is empty).
§Example
let creation_diff = r#"
```diff
--- a/new_file.txt
+++ b/new_file.txt
@@ -0,0 +1,2 @@
+Hello
+World
```
"#;
let patch = parse_single_patch(creation_diff).unwrap();
assert!(patch.is_creation());Sourcepub fn is_deletion(&self) -> bool
pub fn is_deletion(&self) -> bool
Checks if the patch represents a full file deletion.
A patch is considered a deletion if it contains at least one hunk, and all of its hunks result in removing content without adding any new content (i.e., their “replace blocks” are empty). This is typical for a diff that empties a file.
§Example
let deletion_diff = r#"
```diff
--- a/old_file.txt
+++ b/old_file.txt
@@ -1,2 +0,0 @@
-Hello
-World
```
"#;
let patch = parse_single_patch(deletion_diff).unwrap();
assert!(patch.is_deletion());Trait Implementations§
Source§impl Display for Patch
impl Display for Patch
Source§fn fmt(&self, f: &mut Formatter<'_>) -> Result
fn fmt(&self, f: &mut Formatter<'_>) -> Result
Formats the patch into a valid unified diff string for a single file.
This provides a canonical string representation of the entire patch,
including the --- and +++ file headers, followed by the
formatted content of all its hunks. It also correctly handles the
\ No newline at end of file marker when necessary.
This is useful for logging, debugging, or serializing a Patch object
back to its original text format.
§Example
let patch = Patch {
file_path: "src/main.rs".into(),
hunks: vec![Hunk {
lines: vec![
"-old".to_string(),
"+new".to_string(),
],
old_start_line: Some(1),
new_start_line: Some(1),
}],
ends_with_newline: false, // To test the marker
};
let expected_output = concat!(
"--- a/src/main.rs\n",
"+++ b/src/main.rs\n",
"@@ -1,1 +1,1 @@\n",
"-old\n",
"+new\n",
"\\ No newline at end of file"
);
assert_eq!(patch.to_string(), expected_output);impl Eq for Patch
impl StructuralPartialEq for Patch
Auto Trait Implementations§
impl Freeze for Patch
impl RefUnwindSafe for Patch
impl Send for Patch
impl Sync for Patch
impl Unpin for Patch
impl UnsafeUnpin for Patch
impl UnwindSafe for Patch
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more