diff options
Diffstat (limited to 'vendor/git2/src/patch.rs')
-rw-r--r-- | vendor/git2/src/patch.rs | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/vendor/git2/src/patch.rs b/vendor/git2/src/patch.rs new file mode 100644 index 0000000..67b84c0 --- /dev/null +++ b/vendor/git2/src/patch.rs @@ -0,0 +1,235 @@ +use libc::{c_int, c_void}; +use std::marker::PhantomData; +use std::path::Path; +use std::ptr; + +use crate::diff::{print_cb, LineCb}; +use crate::util::{into_opt_c_string, Binding}; +use crate::{raw, Blob, Buf, Diff, DiffDelta, DiffHunk, DiffLine, DiffOptions, Error}; + +/// A structure representing the text changes in a single diff delta. +/// +/// This is an opaque structure. +pub struct Patch<'buffers> { + raw: *mut raw::git_patch, + buffers: PhantomData<&'buffers ()>, +} + +unsafe impl<'buffers> Send for Patch<'buffers> {} + +impl<'buffers> Binding for Patch<'buffers> { + type Raw = *mut raw::git_patch; + unsafe fn from_raw(raw: Self::Raw) -> Self { + Patch { + raw, + buffers: PhantomData, + } + } + fn raw(&self) -> Self::Raw { + self.raw + } +} + +impl<'buffers> Drop for Patch<'buffers> { + fn drop(&mut self) { + unsafe { raw::git_patch_free(self.raw) } + } +} + +impl<'buffers> Patch<'buffers> { + /// Return a Patch for one file in a Diff. + /// + /// Returns Ok(None) for an unchanged or binary file. + pub fn from_diff(diff: &Diff<'buffers>, idx: usize) -> Result<Option<Self>, Error> { + let mut ret = ptr::null_mut(); + unsafe { + try_call!(raw::git_patch_from_diff(&mut ret, diff.raw(), idx)); + Ok(Binding::from_raw_opt(ret)) + } + } + + /// Generate a Patch by diffing two blobs. + pub fn from_blobs( + old_blob: &Blob<'buffers>, + old_path: Option<&Path>, + new_blob: &Blob<'buffers>, + new_path: Option<&Path>, + opts: Option<&mut DiffOptions>, + ) -> Result<Self, Error> { + let mut ret = ptr::null_mut(); + let old_path = into_opt_c_string(old_path)?; + let new_path = into_opt_c_string(new_path)?; + unsafe { + try_call!(raw::git_patch_from_blobs( + &mut ret, + old_blob.raw(), + old_path, + new_blob.raw(), + new_path, + opts.map(|s| s.raw()) + )); + Ok(Binding::from_raw(ret)) + } + } + + /// Generate a Patch by diffing a blob and a buffer. + pub fn from_blob_and_buffer( + old_blob: &Blob<'buffers>, + old_path: Option<&Path>, + new_buffer: &'buffers [u8], + new_path: Option<&Path>, + opts: Option<&mut DiffOptions>, + ) -> Result<Self, Error> { + let mut ret = ptr::null_mut(); + let old_path = into_opt_c_string(old_path)?; + let new_path = into_opt_c_string(new_path)?; + unsafe { + try_call!(raw::git_patch_from_blob_and_buffer( + &mut ret, + old_blob.raw(), + old_path, + new_buffer.as_ptr() as *const c_void, + new_buffer.len(), + new_path, + opts.map(|s| s.raw()) + )); + Ok(Binding::from_raw(ret)) + } + } + + /// Generate a Patch by diffing two buffers. + pub fn from_buffers( + old_buffer: &'buffers [u8], + old_path: Option<&Path>, + new_buffer: &'buffers [u8], + new_path: Option<&Path>, + opts: Option<&mut DiffOptions>, + ) -> Result<Self, Error> { + crate::init(); + let mut ret = ptr::null_mut(); + let old_path = into_opt_c_string(old_path)?; + let new_path = into_opt_c_string(new_path)?; + unsafe { + try_call!(raw::git_patch_from_buffers( + &mut ret, + old_buffer.as_ptr() as *const c_void, + old_buffer.len(), + old_path, + new_buffer.as_ptr() as *const c_void, + new_buffer.len(), + new_path, + opts.map(|s| s.raw()) + )); + Ok(Binding::from_raw(ret)) + } + } + + /// Get the DiffDelta associated with the Patch. + pub fn delta(&self) -> DiffDelta<'buffers> { + unsafe { Binding::from_raw(raw::git_patch_get_delta(self.raw) as *mut _) } + } + + /// Get the number of hunks in the Patch. + pub fn num_hunks(&self) -> usize { + unsafe { raw::git_patch_num_hunks(self.raw) } + } + + /// Get the number of lines of context, additions, and deletions in the Patch. + pub fn line_stats(&self) -> Result<(usize, usize, usize), Error> { + let mut context = 0; + let mut additions = 0; + let mut deletions = 0; + unsafe { + try_call!(raw::git_patch_line_stats( + &mut context, + &mut additions, + &mut deletions, + self.raw + )); + } + Ok((context, additions, deletions)) + } + + /// Get a DiffHunk and its total line count from the Patch. + pub fn hunk(&self, hunk_idx: usize) -> Result<(DiffHunk<'buffers>, usize), Error> { + let mut ret = ptr::null(); + let mut lines = 0; + unsafe { + try_call!(raw::git_patch_get_hunk( + &mut ret, &mut lines, self.raw, hunk_idx + )); + Ok((Binding::from_raw(ret), lines)) + } + } + + /// Get the number of lines in a hunk. + pub fn num_lines_in_hunk(&self, hunk_idx: usize) -> Result<usize, Error> { + unsafe { Ok(try_call!(raw::git_patch_num_lines_in_hunk(self.raw, hunk_idx)) as usize) } + } + + /// Get a DiffLine from a hunk of the Patch. + pub fn line_in_hunk( + &self, + hunk_idx: usize, + line_of_hunk: usize, + ) -> Result<DiffLine<'buffers>, Error> { + let mut ret = ptr::null(); + unsafe { + try_call!(raw::git_patch_get_line_in_hunk( + &mut ret, + self.raw, + hunk_idx, + line_of_hunk + )); + Ok(Binding::from_raw(ret)) + } + } + + /// Get the size of a Patch's diff data in bytes. + pub fn size( + &self, + include_context: bool, + include_hunk_headers: bool, + include_file_headers: bool, + ) -> usize { + unsafe { + raw::git_patch_size( + self.raw, + include_context as c_int, + include_hunk_headers as c_int, + include_file_headers as c_int, + ) + } + } + + /// Print the Patch to text via a callback. + pub fn print(&mut self, mut line_cb: &mut LineCb<'_>) -> Result<(), Error> { + let ptr = &mut line_cb as *mut _ as *mut c_void; + unsafe { + let cb: raw::git_diff_line_cb = Some(print_cb); + try_call!(raw::git_patch_print(self.raw, cb, ptr)); + Ok(()) + } + } + + /// Get the Patch text as a Buf. + pub fn to_buf(&mut self) -> Result<Buf, Error> { + let buf = Buf::new(); + unsafe { + try_call!(raw::git_patch_to_buf(buf.raw(), self.raw)); + } + Ok(buf) + } +} + +impl<'buffers> std::fmt::Debug for Patch<'buffers> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + let mut ds = f.debug_struct("Patch"); + ds.field("delta", &self.delta()) + .field("num_hunks", &self.num_hunks()); + if let Ok(line_stats) = &self.line_stats() { + ds.field("line_stats", line_stats); + } + ds.finish() + } +} |