|
| 1 | +use std::ffi::CString; |
| 2 | +use std::{mem, ptr}; |
| 3 | + |
| 4 | +use crate::util::Binding; |
| 5 | +use crate::{raw, Buf, Commit, DiffFindOptions, DiffOptions, Error, IntoCString}; |
| 6 | +use crate::{Diff, Oid, Signature}; |
| 7 | + |
| 8 | +/// A structure to represent patch in mbox format for sending via email |
| 9 | +pub struct Email { |
| 10 | + buf: Buf, |
| 11 | +} |
| 12 | + |
| 13 | +/// Options for controlling the formatting of the generated e-mail. |
| 14 | +pub struct EmailCreateOptions { |
| 15 | + diff_options: DiffOptions, |
| 16 | + diff_find_options: DiffFindOptions, |
| 17 | + subject_prefix: Option<CString>, |
| 18 | + raw: raw::git_email_create_options, |
| 19 | +} |
| 20 | + |
| 21 | +impl Default for EmailCreateOptions { |
| 22 | + fn default() -> Self { |
| 23 | + // Defaults options created in corresponding to `GIT_EMAIL_CREATE_OPTIONS_INIT` |
| 24 | + let default_options = raw::git_email_create_options { |
| 25 | + version: raw::GIT_EMAIL_CREATE_OPTIONS_VERSION, |
| 26 | + flags: raw::GIT_EMAIL_CREATE_DEFAULT as u32, |
| 27 | + diff_opts: unsafe { mem::zeroed() }, |
| 28 | + diff_find_opts: unsafe { mem::zeroed() }, |
| 29 | + subject_prefix: ptr::null(), |
| 30 | + start_number: 1, |
| 31 | + reroll_number: 0, |
| 32 | + }; |
| 33 | + let mut diff_options = DiffOptions::new(); |
| 34 | + diff_options.show_binary(true).context_lines(3); |
| 35 | + Self { |
| 36 | + diff_options, |
| 37 | + diff_find_options: DiffFindOptions::new(), |
| 38 | + subject_prefix: None, |
| 39 | + raw: default_options, |
| 40 | + } |
| 41 | + } |
| 42 | +} |
| 43 | + |
| 44 | +impl EmailCreateOptions { |
| 45 | + /// Creates a new set of email create options |
| 46 | + /// |
| 47 | + /// By default, options include rename detection and binary |
| 48 | + /// diffs to match `git format-patch`. |
| 49 | + pub fn new() -> Self { |
| 50 | + Self::default() |
| 51 | + } |
| 52 | + |
| 53 | + fn flag(&mut self, opt: raw::git_email_create_flags_t, val: bool) -> &mut Self { |
| 54 | + let opt = opt as u32; |
| 55 | + if val { |
| 56 | + self.raw.flags |= opt; |
| 57 | + } else { |
| 58 | + self.raw.flags &= !opt; |
| 59 | + } |
| 60 | + self |
| 61 | + } |
| 62 | + |
| 63 | + /// Flag indicating whether patch numbers are included in the subject prefix. |
| 64 | + pub fn omit_numbers(&mut self, omit: bool) -> &mut Self { |
| 65 | + self.flag(raw::GIT_EMAIL_CREATE_OMIT_NUMBERS, omit) |
| 66 | + } |
| 67 | + |
| 68 | + /// Flag indicating whether numbers included in the subject prefix even when |
| 69 | + /// the patch is for a single commit (1/1). |
| 70 | + pub fn always_number(&mut self, always: bool) -> &mut Self { |
| 71 | + self.flag(raw::GIT_EMAIL_CREATE_ALWAYS_NUMBER, always) |
| 72 | + } |
| 73 | + |
| 74 | + /// Flag indicating whether rename or similarity detection are ignored. |
| 75 | + pub fn ignore_renames(&mut self, ignore: bool) -> &mut Self { |
| 76 | + self.flag(raw::GIT_EMAIL_CREATE_NO_RENAMES, ignore) |
| 77 | + } |
| 78 | + |
| 79 | + /// Get mutable access to `DiffOptions` that are used for creating diffs. |
| 80 | + pub fn diff_options(&mut self) -> &mut DiffOptions { |
| 81 | + &mut self.diff_options |
| 82 | + } |
| 83 | + |
| 84 | + /// Get mutable access to `DiffFindOptions` that are used for finding |
| 85 | + /// similarities within diffs. |
| 86 | + pub fn diff_find_options(&mut self) -> &mut DiffFindOptions { |
| 87 | + &mut self.diff_find_options |
| 88 | + } |
| 89 | + |
| 90 | + /// Set the subject prefix |
| 91 | + /// |
| 92 | + /// The default value for this is "PATCH". If set to an empty string ("") |
| 93 | + /// then only the patch numbers will be shown in the prefix. |
| 94 | + /// If the subject_prefix is empty and patch numbers are not being shown, |
| 95 | + /// the prefix will be omitted entirely. |
| 96 | + pub fn subject_prefix<T: IntoCString>(&mut self, t: T) -> &mut Self { |
| 97 | + self.subject_prefix = Some(t.into_c_string().unwrap()); |
| 98 | + self |
| 99 | + } |
| 100 | + |
| 101 | + /// Set the starting patch number; this cannot be 0. |
| 102 | + /// |
| 103 | + /// The default value for this is 1. |
| 104 | + pub fn start_number(&mut self, number: usize) -> &mut Self { |
| 105 | + self.raw.start_number = number; |
| 106 | + self |
| 107 | + } |
| 108 | + |
| 109 | + /// Set the "re-roll" number. |
| 110 | + /// |
| 111 | + /// The default value for this is 0 (no re-roll). |
| 112 | + pub fn reroll_number(&mut self, number: usize) -> &mut Self { |
| 113 | + self.raw.reroll_number = number; |
| 114 | + self |
| 115 | + } |
| 116 | + |
| 117 | + /// Acquire a pointer to the underlying raw options. |
| 118 | + /// |
| 119 | + /// This function is unsafe as the pointer is only valid so long as this |
| 120 | + /// structure is not moved, modified, or used elsewhere. |
| 121 | + unsafe fn raw(&mut self) -> *const raw::git_email_create_options { |
| 122 | + self.raw.subject_prefix = self |
| 123 | + .subject_prefix |
| 124 | + .as_ref() |
| 125 | + .map(|s| s.as_ptr()) |
| 126 | + .unwrap_or(ptr::null()); |
| 127 | + self.raw.diff_opts = ptr::read(self.diff_options.raw()); |
| 128 | + self.raw.diff_find_opts = ptr::read(self.diff_find_options.raw()); |
| 129 | + &self.raw as *const _ |
| 130 | + } |
| 131 | +} |
| 132 | + |
| 133 | +impl Email { |
| 134 | + /// Returns a byte slice with stored e-mail patch in. `Email` could be |
| 135 | + /// created by one of the `from_*` functions. |
| 136 | + pub fn as_slice(&self) -> &[u8] { |
| 137 | + &self.buf |
| 138 | + } |
| 139 | + |
| 140 | + /// Create a diff for a commit in mbox format for sending via email. |
| 141 | + pub fn from_diff<T: IntoCString>( |
| 142 | + diff: &Diff<'_>, |
| 143 | + patch_idx: usize, |
| 144 | + patch_count: usize, |
| 145 | + commit_id: &Oid, |
| 146 | + summary: T, |
| 147 | + body: T, |
| 148 | + author: &Signature<'_>, |
| 149 | + opts: &mut EmailCreateOptions, |
| 150 | + ) -> Result<Self, Error> { |
| 151 | + let buf = Buf::new(); |
| 152 | + let summary = summary.into_c_string()?; |
| 153 | + let body = body.into_c_string()?; |
| 154 | + unsafe { |
| 155 | + try_call!(raw::git_email_create_from_diff( |
| 156 | + buf.raw(), |
| 157 | + Binding::raw(diff), |
| 158 | + patch_idx, |
| 159 | + patch_count, |
| 160 | + Binding::raw(commit_id), |
| 161 | + summary.as_ptr(), |
| 162 | + body.as_ptr(), |
| 163 | + Binding::raw(author), |
| 164 | + opts.raw() |
| 165 | + )); |
| 166 | + Ok(Self { buf }) |
| 167 | + } |
| 168 | + } |
| 169 | + |
| 170 | + /// Create a diff for a commit in mbox format for sending via email. |
| 171 | + /// The commit must not be a merge commit. |
| 172 | + pub fn from_commit(commit: &Commit<'_>, opts: &mut EmailCreateOptions) -> Result<Self, Error> { |
| 173 | + let buf = Buf::new(); |
| 174 | + unsafe { |
| 175 | + try_call!(raw::git_email_create_from_commit( |
| 176 | + buf.raw(), |
| 177 | + commit.raw(), |
| 178 | + opts.raw() |
| 179 | + )); |
| 180 | + Ok(Self { buf }) |
| 181 | + } |
| 182 | + } |
| 183 | +} |
0 commit comments