Skip to content

Commit

Permalink
feat: control filenames in DiffOptions (#36)
Browse files Browse the repository at this point in the history
Add the ability to change the filenames in patches produced by `DiffOptions`.

---------

Co-authored-by: Brandon Williams <[email protected]>
  • Loading branch information
eholk and bmwill authored Jan 29, 2025
1 parent b36b981 commit 26322ac
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 3 deletions.
88 changes: 85 additions & 3 deletions src/diff/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
range::{DiffRange, SliceLike},
utils::Classifier,
};
use std::{cmp, ops};
use std::{borrow::Cow, cmp, ops};

mod cleanup;
mod myers;
Expand Down Expand Up @@ -46,6 +46,8 @@ where
pub struct DiffOptions {
compact: bool,
context_len: usize,
original_filename: Option<Cow<'static, str>>,
modified_filename: Option<Cow<'static, str>>,
}

impl DiffOptions {
Expand All @@ -57,6 +59,8 @@ impl DiffOptions {
Self {
compact: true,
context_len: 3,
original_filename: Some("original".into()),
modified_filename: Some("modified".into()),
}
}

Expand All @@ -76,6 +80,28 @@ impl DiffOptions {
self
}

/// Set the filename to be used in the patch for the original text
///
/// If not set, the default value is "original".
pub fn set_original_filename<T>(&mut self, filename: T) -> &mut Self
where
T: Into<Cow<'static, str>>,
{
self.original_filename = Some(filename.into());
self
}

/// Set the filename to be used in the patch for the modified text
///
/// If not set, the default value is "modified".
pub fn set_modified_filename<T>(&mut self, filename: T) -> &mut Self
where
T: Into<Cow<'static, str>>,
{
self.modified_filename = Some(filename.into());
self
}

// TODO determine if this should be exposed in the public API
#[allow(dead_code)]
fn diff<'a>(&self, original: &'a str, modified: &'a str) -> Vec<Diff<'a, str>> {
Expand All @@ -102,7 +128,11 @@ impl DiffOptions {
let solution = self.diff_slice(&old_ids, &new_ids);

let hunks = to_hunks(&old_lines, &new_lines, &solution, self.context_len);
Patch::new(Some("original"), Some("modified"), hunks)
Patch::new(
self.original_filename.clone(),
self.modified_filename.clone(),
hunks,
)
}

/// Create a patch between two potentially non-utf8 texts
Expand All @@ -118,7 +148,20 @@ impl DiffOptions {
let solution = self.diff_slice(&old_ids, &new_ids);

let hunks = to_hunks(&old_lines, &new_lines, &solution, self.context_len);
Patch::new(Some(&b"original"[..]), Some(&b"modified"[..]), hunks)

// helper function to convert a utf8 cow to a bytes cow
fn cow_str_to_bytes(cow: Cow<'static, str>) -> Cow<'static, [u8]> {
match cow {
Cow::Borrowed(b) => Cow::Borrowed(b.as_bytes()),
Cow::Owned(o) => Cow::Owned(o.into_bytes()),
}
}

Patch::new(
self.original_filename.clone().map(cow_str_to_bytes),
self.modified_filename.clone().map(cow_str_to_bytes),
hunks,
)
}

pub(crate) fn diff_slice<'a, T: PartialEq>(
Expand Down Expand Up @@ -358,3 +401,42 @@ fn build_edit_script<T>(solution: &[DiffRange<[T]>]) -> Vec<EditRange> {

edit_script
}

#[cfg(test)]
mod test {
use super::DiffOptions;

#[test]
fn set_original_and_modified_filenames() {
let original = "\
I am afraid, however, that all I have known - that my story - will be forgotten.
I am afraid for the world that is to come.
Afraid that my plans will fail.
Afraid of a doom worse than the Deepness.
";
let modified = "\
I am afraid, however, that all I have known - that my story - will be forgotten.
I am afraid for the world that is to come.
Afraid that Alendi will fail.
Afraid of a doom brought by the Deepness.
";
let expected = "\
--- the old version
+++ the better version
@@ -1,4 +1,4 @@
I am afraid, however, that all I have known - that my story - will be forgotten.
I am afraid for the world that is to come.
-Afraid that my plans will fail.
-Afraid of a doom worse than the Deepness.
+Afraid that Alendi will fail.
+Afraid of a doom brought by the Deepness.
";

let patch = DiffOptions::new()
.set_original_filename("the old version")
.set_modified_filename("the better version")
.create_patch(original, modified);

assert_eq!(patch.to_string(), expected);
}
}
1 change: 1 addition & 0 deletions src/patch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ where
struct Filename<'a, T: ToOwned + ?Sized>(Cow<'a, T>);

const ESCAPED_CHARS: &[char] = &['\n', '\t', '\0', '\r', '\"', '\\'];
#[allow(clippy::byte_char_slices)]
const ESCAPED_CHARS_BYTES: &[u8] = &[b'\n', b'\t', b'\0', b'\r', b'\"', b'\\'];

impl Filename<'_, str> {
Expand Down

0 comments on commit 26322ac

Please sign in to comment.