1use libc::{c_int, c_void};
2use std::marker::PhantomData;
3use std::path::Path;
4use std::ptr;
5
6use crate::diff::{print_cb, LineCb};
7use crate::util::{into_opt_c_string, Binding};
8use crate::{raw, Blob, Buf, Diff, DiffDelta, DiffHunk, DiffLine, DiffOptions, Error};
9
10pub struct Patch<'buffers> {
14 raw: *mut raw::git_patch,
15 buffers: PhantomData<&'buffers ()>,
16}
17
18unsafe impl<'buffers> Send for Patch<'buffers> {}
19
20impl<'buffers> Binding for Patch<'buffers> {
21 type Raw = *mut raw::git_patch;
22 unsafe fn from_raw(raw: Self::Raw) -> Self {
23 Patch {
24 raw,
25 buffers: PhantomData,
26 }
27 }
28 fn raw(&self) -> Self::Raw {
29 self.raw
30 }
31}
32
33impl<'buffers> Drop for Patch<'buffers> {
34 fn drop(&mut self) {
35 unsafe { raw::git_patch_free(self.raw) }
36 }
37}
38
39impl<'buffers> Patch<'buffers> {
40 pub fn from_diff(diff: &Diff<'buffers>, idx: usize) -> Result<Option<Self>, Error> {
44 let mut ret = ptr::null_mut();
45 unsafe {
46 try_call!(raw::git_patch_from_diff(&mut ret, diff.raw(), idx));
47 Ok(Binding::from_raw_opt(ret))
48 }
49 }
50
51 pub fn from_blobs(
53 old_blob: &Blob<'buffers>,
54 old_path: Option<&Path>,
55 new_blob: &Blob<'buffers>,
56 new_path: Option<&Path>,
57 opts: Option<&mut DiffOptions>,
58 ) -> Result<Self, Error> {
59 let mut ret = ptr::null_mut();
60 let old_path = into_opt_c_string(old_path)?;
61 let new_path = into_opt_c_string(new_path)?;
62 unsafe {
63 try_call!(raw::git_patch_from_blobs(
64 &mut ret,
65 old_blob.raw(),
66 old_path,
67 new_blob.raw(),
68 new_path,
69 opts.map(|s| s.raw())
70 ));
71 Ok(Binding::from_raw(ret))
72 }
73 }
74
75 pub fn from_blob_and_buffer(
77 old_blob: &Blob<'buffers>,
78 old_path: Option<&Path>,
79 new_buffer: &'buffers [u8],
80 new_path: Option<&Path>,
81 opts: Option<&mut DiffOptions>,
82 ) -> Result<Self, Error> {
83 let mut ret = ptr::null_mut();
84 let old_path = into_opt_c_string(old_path)?;
85 let new_path = into_opt_c_string(new_path)?;
86 unsafe {
87 try_call!(raw::git_patch_from_blob_and_buffer(
88 &mut ret,
89 old_blob.raw(),
90 old_path,
91 new_buffer.as_ptr() as *const c_void,
92 new_buffer.len(),
93 new_path,
94 opts.map(|s| s.raw())
95 ));
96 Ok(Binding::from_raw(ret))
97 }
98 }
99
100 pub fn from_buffers(
102 old_buffer: &'buffers [u8],
103 old_path: Option<&Path>,
104 new_buffer: &'buffers [u8],
105 new_path: Option<&Path>,
106 opts: Option<&mut DiffOptions>,
107 ) -> Result<Self, Error> {
108 crate::init();
109 let mut ret = ptr::null_mut();
110 let old_path = into_opt_c_string(old_path)?;
111 let new_path = into_opt_c_string(new_path)?;
112 unsafe {
113 try_call!(raw::git_patch_from_buffers(
114 &mut ret,
115 old_buffer.as_ptr() as *const c_void,
116 old_buffer.len(),
117 old_path,
118 new_buffer.as_ptr() as *const c_void,
119 new_buffer.len(),
120 new_path,
121 opts.map(|s| s.raw())
122 ));
123 Ok(Binding::from_raw(ret))
124 }
125 }
126
127 pub fn delta<'a>(&'a self) -> DiffDelta<'a> {
129 unsafe { Binding::from_raw(raw::git_patch_get_delta(self.raw) as *mut _) }
130 }
131
132 pub fn num_hunks(&self) -> usize {
134 unsafe { raw::git_patch_num_hunks(self.raw) }
135 }
136
137 pub fn line_stats(&self) -> Result<(usize, usize, usize), Error> {
139 let mut context = 0;
140 let mut additions = 0;
141 let mut deletions = 0;
142 unsafe {
143 try_call!(raw::git_patch_line_stats(
144 &mut context,
145 &mut additions,
146 &mut deletions,
147 self.raw
148 ));
149 }
150 Ok((context, additions, deletions))
151 }
152
153 pub fn hunk<'a>(&'a self, hunk_idx: usize) -> Result<(DiffHunk<'a>, usize), Error> {
155 let mut ret = ptr::null();
156 let mut lines = 0;
157 unsafe {
158 try_call!(raw::git_patch_get_hunk(
159 &mut ret, &mut lines, self.raw, hunk_idx
160 ));
161 Ok((Binding::from_raw(ret), lines))
162 }
163 }
164
165 pub fn num_lines_in_hunk(&self, hunk_idx: usize) -> Result<usize, Error> {
167 unsafe { Ok(try_call!(raw::git_patch_num_lines_in_hunk(self.raw, hunk_idx)) as usize) }
168 }
169
170 pub fn line_in_hunk<'a>(
172 &'a self,
173 hunk_idx: usize,
174 line_of_hunk: usize,
175 ) -> Result<DiffLine<'a>, Error> {
176 let mut ret = ptr::null();
177 unsafe {
178 try_call!(raw::git_patch_get_line_in_hunk(
179 &mut ret,
180 self.raw,
181 hunk_idx,
182 line_of_hunk
183 ));
184 Ok(Binding::from_raw(ret))
185 }
186 }
187
188 pub fn size(
190 &self,
191 include_context: bool,
192 include_hunk_headers: bool,
193 include_file_headers: bool,
194 ) -> usize {
195 unsafe {
196 raw::git_patch_size(
197 self.raw,
198 include_context as c_int,
199 include_hunk_headers as c_int,
200 include_file_headers as c_int,
201 )
202 }
203 }
204
205 pub fn print(&mut self, mut line_cb: &mut LineCb<'_>) -> Result<(), Error> {
207 let ptr = &mut line_cb as *mut _ as *mut c_void;
208 unsafe {
209 let cb: raw::git_diff_line_cb = Some(print_cb);
210 try_call!(raw::git_patch_print(self.raw, cb, ptr));
211 Ok(())
212 }
213 }
214
215 pub fn to_buf(&mut self) -> Result<Buf, Error> {
217 let buf = Buf::new();
218 unsafe {
219 try_call!(raw::git_patch_to_buf(buf.raw(), self.raw));
220 }
221 Ok(buf)
222 }
223}
224
225impl<'buffers> std::fmt::Debug for Patch<'buffers> {
226 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
227 let mut ds = f.debug_struct("Patch");
228 ds.field("delta", &self.delta())
229 .field("num_hunks", &self.num_hunks());
230 if let Ok(line_stats) = &self.line_stats() {
231 ds.field("line_stats", line_stats);
232 }
233 ds.finish()
234 }
235}