patchkit/lib.rs
1#![deny(missing_docs)]
2//! A crate for parsing and manipulating patches.
3//!
4//! # Examples
5//!
6//! ```
7//! use patchkit::ContentPatch;
8//! use patchkit::unified::parse_patch;
9//! use patchkit::unified::{UnifiedPatch, Hunk, HunkLine};
10//!
11//! let patch = UnifiedPatch::parse_patch(vec![
12//! "--- a/file1\n",
13//! "+++ b/file1\n",
14//! "@@ -1,1 +1,1 @@\n",
15//! "-a\n",
16//! "+b\n",
17//! ].into_iter().map(|s| s.as_bytes())).unwrap();
18//!
19//! assert_eq!(patch, UnifiedPatch {
20//! orig_name: b"a/file1".to_vec(),
21//! mod_name: b"b/file1".to_vec(),
22//! orig_ts: None,
23//! mod_ts: None,
24//! hunks: vec![
25//! Hunk {
26//! mod_pos: 1,
27//! mod_range: 1,
28//! orig_pos: 1,
29//! orig_range: 1,
30//! lines: vec![
31//! HunkLine::RemoveLine(b"a\n".to_vec()),
32//! HunkLine::InsertLine(b"b\n".to_vec()),
33//! ],
34//! tail: None
35//! },
36//! ],
37//! });
38//!
39//! let applied = patch.apply_exact(&b"a\n"[..]).unwrap();
40//! assert_eq!(applied, b"b\n");
41//! ```
42
43pub mod ed;
44pub mod quilt;
45pub mod timestamp;
46pub mod unified;
47
48/// Strip the specified number of path components from the beginning of the path.
49pub fn strip_prefix(path: &std::path::Path, prefix: usize) -> &std::path::Path {
50 let mut components = path.components();
51 for _ in 0..prefix {
52 components.next();
53 }
54 std::path::Path::new(components.as_path())
55}
56
57/// Error that occurs when applying a patch
58#[derive(Debug)]
59pub enum ApplyError {
60 /// A conflict occurred
61 Conflict(String),
62
63 /// The patch is unapplyable
64 Unapplyable,
65}
66
67impl std::fmt::Display for ApplyError {
68 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
69 match self {
70 Self::Conflict(reason) => write!(f, "Conflict: {}", reason),
71 Self::Unapplyable => write!(f, "Patch unapplyable"),
72 }
73 }
74}
75
76impl std::error::Error for ApplyError {}
77
78/// A patch to a single file
79pub trait SingleFilePatch: ContentPatch {
80 /// Old file name
81 fn oldname(&self) -> &[u8];
82
83 /// New file name
84 fn newname(&self) -> &[u8];
85}
86
87/// A patch that can be applied to file content
88pub trait ContentPatch {
89 /// Apply this patch to a file
90 fn apply_exact(&self, orig: &[u8]) -> Result<Vec<u8>, ApplyError>;
91}
92
93#[test]
94fn test_strip_prefix() {
95 assert_eq!(
96 std::path::PathBuf::from("b"),
97 strip_prefix(std::path::Path::new("a/b"), 1)
98 );
99 assert_eq!(
100 std::path::PathBuf::from("a/b"),
101 strip_prefix(std::path::Path::new("a/b"), 0)
102 );
103 assert_eq!(
104 std::path::PathBuf::from(""),
105 strip_prefix(std::path::Path::new("a/b"), 2)
106 );
107}