Skip to content

Commit

Permalink
Merge pull request #26 from breezy-team/refactor
Browse files Browse the repository at this point in the history
Refactor
  • Loading branch information
jelmer authored Nov 16, 2024
2 parents e85ef06 + 96d910d commit 6ff1ad3
Show file tree
Hide file tree
Showing 2 changed files with 267 additions and 21 deletions.
210 changes: 209 additions & 1 deletion src/quilt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ mod find_common_patch_suffix_tests {
}

/// A entry in a series file
#[derive(Debug)]
#[derive(Debug, PartialEq, Eq)]
pub enum SeriesEntry {
/// A patch entry
Patch {
Expand Down Expand Up @@ -214,6 +214,19 @@ impl Series {

Ok(())
}

/// Get an iterator over the entries in the series file
pub fn iter(&self) -> std::slice::Iter<SeriesEntry> {
self.entries.iter()
}
}

impl std::ops::Index<usize> for Series {
type Output = SeriesEntry;

fn index(&self, index: usize) -> &Self::Output {
&self.entries[index]
}
}

impl Default for Series {
Expand All @@ -235,3 +248,198 @@ pub fn read_quilt_series<R: std::io::Read>(mut reader: R) -> std::path::PathBuf
reader.read_to_string(&mut s).unwrap();
s.into()
}

/// A quilt patch
pub struct QuiltPatch {
/// The name of the patch
pub name: String,

/// The options for the patch
pub options: Vec<String>,

/// The patch contents
pub patch: Vec<u8>,
}

impl QuiltPatch {
/// Get the patch contents as a byte slice
pub fn as_bytes(&self) -> &[u8] {
&self.patch
}

/// Get the name of the patch
pub fn name(&self) -> &str {
&self.name
}

/// Get the patch options
pub fn options(&self) -> &[String] {
&self.options
}

/// Get the patch contents
pub fn parse(&self) -> Result<Vec<crate::unified::UnifiedPatch>, crate::unified::Error> {
let lines = self.patch.split(|&b| b == b'\n');
crate::unified::parse_patches(lines.map(|x| x.to_vec()))
.filter_map(|patch| match patch {
Ok(crate::unified::PlainOrBinaryPatch::Plain(patch)) => Some(Ok(patch)),
Ok(crate::unified::PlainOrBinaryPatch::Binary(_)) => None,
Err(err) => Some(Err(err)),
})
.collect()
}
}

/// Read quilt patches from a directory.
pub fn iter_quilt_patches(directory: &std::path::Path) -> impl Iterator<Item = QuiltPatch> + '_ {
let series_path = directory.join("series");

let series = if series_path.exists() {
Series::read(std::fs::File::open(series_path).unwrap()).unwrap()
} else {
Series::new()
};

series
.iter()
.filter_map(move |entry| {
let (patch, options) = match entry {
SeriesEntry::Patch { name, options } => (name, options),
SeriesEntry::Comment(_) => return None,
};
let p = directory.join(patch);
let lines = std::fs::read_to_string(p).unwrap();
Some(QuiltPatch {
name: patch.to_string(),
patch: lines.into_bytes(),
options: options.clone(),
})
})
.collect::<Vec<_>>()
.into_iter()
}

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

#[test]
fn test_series_read() {
let series = Series::read(
r#"0001-foo.patch
# This is a comment
0002-bar.patch --reverse
0003-baz.patch --reverse --fuzz=3
"#
.as_bytes(),
)
.unwrap();
assert_eq!(series.len(), 3);
assert_eq!(
series[0],
SeriesEntry::Patch {
name: "0001-foo.patch".to_string(),
options: vec![]
}
);
assert_eq!(
series[1],
SeriesEntry::Comment("# This is a comment".to_string())
);
assert_eq!(
series[2],
SeriesEntry::Patch {
name: "0002-bar.patch".to_string(),
options: vec!["--reverse".to_string()]
}
);
assert_eq!(
series[3],
SeriesEntry::Patch {
name: "0003-baz.patch".to_string(),
options: vec!["--reverse".to_string(), "--fuzz=3".to_string()]
}
);
}

#[test]
fn test_series_write() {
let mut series = Series::new();
series.append("0001-foo.patch", None);
series.append("0002-bar.patch", Some(&["--reverse".to_string()]));
series.append(
"0003-baz.patch",
Some(&["--reverse".to_string(), "--fuzz=3".to_string()]),
);

let mut writer = vec![];
series.write(&mut writer).unwrap();
let series = String::from_utf8(writer).unwrap();
assert_eq!(
series,
"0001-foo.patch\n0002-bar.patch --reverse\n0003-baz.patch --reverse --fuzz=3\n"
);
}

#[test]
fn test_series_remove() {
let mut series = Series::new();
series.append("0001-foo.patch", None);
series.append("0002-bar.patch", Some(&["--reverse".to_string()]));
series.append(
"0003-baz.patch",
Some(&["--reverse".to_string(), "--fuzz=3".to_string()]),
);

series.remove("0002-bar.patch");

let mut writer = vec![];
series.write(&mut writer).unwrap();
let series = String::from_utf8(writer).unwrap();
assert_eq!(
series,
"0001-foo.patch\n0003-baz.patch --reverse --fuzz=3\n"
);
}

#[test]
fn test_series_contains() {
let mut series = Series::new();
series.append("0001-foo.patch", None);
series.append("0002-bar.patch", Some(&["--reverse".to_string()]));
series.append(
"0003-baz.patch",
Some(&["--reverse".to_string(), "--fuzz=3".to_string()]),
);

assert!(series.contains("0002-bar.patch"));
assert!(!series.contains("0004-qux.patch"));
}

#[test]
fn test_series_patches() {
let mut series = Series::new();
series.append("0001-foo.patch", None);
series.append("0002-bar.patch", Some(&["--reverse".to_string()]));
series.append(
"0003-baz.patch",
Some(&["--reverse".to_string(), "--fuzz=3".to_string()]),
);

let patches: Vec<_> = series.patches().collect();
assert_eq!(
patches,
&["0001-foo.patch", "0002-bar.patch", "0003-baz.patch"]
);
}

#[test]
fn test_series_is_empty() {
let series = Series::new();
assert!(series.is_empty());

let mut series = Series::new();
series.append("0001-foo.patch", None);
assert!(!series.is_empty());
}
}
78 changes: 58 additions & 20 deletions src/unified.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! Parsing of unified patches
use crate::{ContentPatch, SingleFilePatch};
use regex::bytes::Regex;
use std::num::ParseIntError;

Expand Down Expand Up @@ -330,7 +331,7 @@ mod iter_hunks_tests {
///
/// # Arguments
/// * `iter_lines`: Iterator over lines
pub fn parse_patch<'a, I>(iter_lines: I) -> Result<Box<dyn crate::SingleFilePatch>, Error>
pub fn parse_patch<'a, I>(iter_lines: I) -> Result<PlainOrBinaryPatch, Error>
where
I: Iterator<Item = &'a [u8]> + 'a,
{
Expand All @@ -339,7 +340,7 @@ where
let ((orig_name, orig_ts), (mod_name, mod_ts)) = match get_patch_names(&mut iter_lines) {
Ok(names) => names,
Err(Error::BinaryFiles(orig_name, mod_name)) => {
return Ok(Box::new(BinaryPatch(orig_name, mod_name)));
return Ok(PlainOrBinaryPatch::Binary(BinaryPatch(orig_name, mod_name)));
}
Err(e) => return Err(e),
};
Expand All @@ -348,11 +349,12 @@ where
for hunk in iter_hunks(&mut iter_lines) {
patch.hunks.push(hunk?);
}
Ok(Box::new(patch))
Ok(PlainOrBinaryPatch::Plain(patch))
}

#[cfg(test)]
mod patches_tests {
use super::*;
macro_rules! test_patch {
($name:ident, $orig:expr, $mod:expr, $patch:expr) => {
#[test]
Expand Down Expand Up @@ -826,25 +828,57 @@ mod iter_file_patch_tests {
}
}

/// A patch that can be applied to a single file
pub enum PlainOrBinaryPatch {
/// A unified patch
Plain(UnifiedPatch),

/// An indication that two binary files differ
Binary(BinaryPatch),
}

impl SingleFilePatch for PlainOrBinaryPatch {
fn oldname(&self) -> &[u8] {
match self {
Self::Plain(patch) => patch.orig_name.as_slice(),
Self::Binary(patch) => patch.0.as_slice(),
}
}

fn newname(&self) -> &[u8] {
match self {
Self::Plain(patch) => patch.mod_name.as_slice(),
Self::Binary(patch) => patch.1.as_slice(),
}
}
}

impl crate::ContentPatch for PlainOrBinaryPatch {
fn apply_exact(&self, orig: &[u8]) -> Result<Vec<u8>, crate::ApplyError> {
match self {
Self::Plain(patch) => patch.apply_exact(orig),
Self::Binary(_) => Err(crate::ApplyError::Unapplyable),
}
}
}

/// Parse a patch file
///
/// # Arguments
/// * `iter`: Iterator over lines
pub fn parse_patches<'a, I>(iter: I) -> Result<Vec<Box<dyn crate::SingleFilePatch>>, Error>
pub fn parse_patches<I>(iter: I) -> impl Iterator<Item = Result<PlainOrBinaryPatch, Error>>
where
I: Iterator<Item = Vec<u8>>,
{
iter_file_patch(iter)
.filter_map(|entry| match entry {
Ok(FileEntry::Patch(lines)) => match parse_patch(lines.iter().map(|l| l.as_slice())) {
Ok(patch) => Some(Ok(patch)),
Err(e) => Some(Err(e)),
},
Ok(FileEntry::Junk(_)) => None,
Ok(FileEntry::Meta(_)) => None,
iter_file_patch(iter).filter_map(|entry| match entry {
Ok(FileEntry::Patch(lines)) => match parse_patch(lines.iter().map(|l| l.as_slice())) {
Ok(patch) => Some(Ok(patch)),
Err(e) => Some(Err(e)),
})
.collect()
},
Ok(FileEntry::Junk(_)) => None,
Ok(FileEntry::Meta(_)) => None,
Err(e) => Some(Err(e)),
})
}

#[cfg(test)]
Expand All @@ -860,7 +894,8 @@ mod parse_patches_tests {
" # <[email protected]>\n",
" #\n",
];
let patches = super::parse_patches(lines.iter().map(|l| l.as_bytes().to_vec())).unwrap();
let patches =
super::parse_patches(lines.iter().map(|l| l.as_bytes().to_vec())).collect::<Vec<_>>();
assert_eq!(patches.len(), 1);
}
}
Expand All @@ -869,7 +904,7 @@ mod parse_patches_tests {
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BinaryPatch(pub Vec<u8>, pub Vec<u8>);

impl crate::SingleFilePatch for BinaryPatch {
impl SingleFilePatch for BinaryPatch {
fn oldname(&self) -> &[u8] {
&self.0
}
Expand Down Expand Up @@ -984,27 +1019,30 @@ impl UnifiedPatch {
///
/// # Arguments
/// * `iter`: Iterator over lines
pub fn parse_patches<'a, I>(iter: I) -> Result<Vec<Box<dyn crate::SingleFilePatch>>, Error>
pub fn parse_patches<I>(iter: I) -> Result<Vec<PlainOrBinaryPatch>, Error>
where
I: Iterator<Item = Vec<u8>>,
{
iter_file_patch(iter)
.filter_map(|entry| match entry {
Ok(FileEntry::Patch(lines)) => {
match Self::parse_patch(lines.iter().map(|l| l.as_slice())) {
Ok(patch) => Some(Ok(Box::new(patch) as Box<dyn crate::SingleFilePatch>)),
Ok(patch) => Some(Ok(PlainOrBinaryPatch::Plain(patch))),
Err(e) => Some(Err(e)),
}
}
Ok(FileEntry::Junk(_)) => None,
Ok(FileEntry::Meta(_)) => None,
Err(Error::BinaryFiles(orig_name, mod_name)) => Some(Ok(
PlainOrBinaryPatch::Binary(BinaryPatch(orig_name, mod_name)),
)),
Err(e) => Some(Err(e)),
})
.collect()
}
}

impl crate::SingleFilePatch for UnifiedPatch {
impl SingleFilePatch for UnifiedPatch {
/// Old file name
fn oldname(&self) -> &[u8] {
&self.orig_name
Expand All @@ -1016,7 +1054,7 @@ impl crate::SingleFilePatch for UnifiedPatch {
}
}

impl crate::ContentPatch for UnifiedPatch {
impl ContentPatch for UnifiedPatch {
/// Apply this patch to a file
fn apply_exact(&self, orig: &[u8]) -> Result<Vec<u8>, crate::ApplyError> {
let orig_lines = splitlines(orig).map(|l| l.to_vec());
Expand Down

0 comments on commit 6ff1ad3

Please sign in to comment.