use std::ffi::CString; use std::{mem, ptr}; use crate::util::Binding; use crate::{raw, Buf, Commit, DiffFindOptions, DiffOptions, Error, IntoCString}; use crate::{Diff, Oid, Signature}; /// A structure to represent patch in mbox format for sending via email pub struct Email { buf: Buf, } /// Options for controlling the formatting of the generated e-mail. pub struct EmailCreateOptions { diff_options: DiffOptions, diff_find_options: DiffFindOptions, subject_prefix: Option, raw: raw::git_email_create_options, } impl Default for EmailCreateOptions { fn default() -> Self { // Defaults options created in corresponding to `GIT_EMAIL_CREATE_OPTIONS_INIT` let default_options = raw::git_email_create_options { version: raw::GIT_EMAIL_CREATE_OPTIONS_VERSION, flags: raw::GIT_EMAIL_CREATE_DEFAULT as u32, diff_opts: unsafe { mem::zeroed() }, diff_find_opts: unsafe { mem::zeroed() }, subject_prefix: ptr::null(), start_number: 1, reroll_number: 0, }; let mut diff_options = DiffOptions::new(); diff_options.show_binary(true).context_lines(3); Self { diff_options, diff_find_options: DiffFindOptions::new(), subject_prefix: None, raw: default_options, } } } impl EmailCreateOptions { /// Creates a new set of email create options /// /// By default, options include rename detection and binary /// diffs to match `git format-patch`. pub fn new() -> Self { Self::default() } fn flag(&mut self, opt: raw::git_email_create_flags_t, val: bool) -> &mut Self { let opt = opt as u32; if val { self.raw.flags |= opt; } else { self.raw.flags &= !opt; } self } /// Flag indicating whether patch numbers are included in the subject prefix. pub fn omit_numbers(&mut self, omit: bool) -> &mut Self { self.flag(raw::GIT_EMAIL_CREATE_OMIT_NUMBERS, omit) } /// Flag indicating whether numbers included in the subject prefix even when /// the patch is for a single commit (1/1). pub fn always_number(&mut self, always: bool) -> &mut Self { self.flag(raw::GIT_EMAIL_CREATE_ALWAYS_NUMBER, always) } /// Flag indicating whether rename or similarity detection are ignored. pub fn ignore_renames(&mut self, ignore: bool) -> &mut Self { self.flag(raw::GIT_EMAIL_CREATE_NO_RENAMES, ignore) } /// Get mutable access to `DiffOptions` that are used for creating diffs. pub fn diff_options(&mut self) -> &mut DiffOptions { &mut self.diff_options } /// Get mutable access to `DiffFindOptions` that are used for finding /// similarities within diffs. pub fn diff_find_options(&mut self) -> &mut DiffFindOptions { &mut self.diff_find_options } /// Set the subject prefix /// /// The default value for this is "PATCH". If set to an empty string ("") /// then only the patch numbers will be shown in the prefix. /// If the subject_prefix is empty and patch numbers are not being shown, /// the prefix will be omitted entirely. pub fn subject_prefix(&mut self, t: T) -> &mut Self { self.subject_prefix = Some(t.into_c_string().unwrap()); self } /// Set the starting patch number; this cannot be 0. /// /// The default value for this is 1. pub fn start_number(&mut self, number: usize) -> &mut Self { self.raw.start_number = number; self } /// Set the "re-roll" number. /// /// The default value for this is 0 (no re-roll). pub fn reroll_number(&mut self, number: usize) -> &mut Self { self.raw.reroll_number = number; self } /// Acquire a pointer to the underlying raw options. /// /// This function is unsafe as the pointer is only valid so long as this /// structure is not moved, modified, or used elsewhere. unsafe fn raw(&mut self) -> *const raw::git_email_create_options { self.raw.subject_prefix = self .subject_prefix .as_ref() .map(|s| s.as_ptr()) .unwrap_or(ptr::null()); self.raw.diff_opts = ptr::read(self.diff_options.raw()); self.raw.diff_find_opts = ptr::read(self.diff_find_options.raw()); &self.raw as *const _ } } impl Email { /// Returns a byte slice with stored e-mail patch in. `Email` could be /// created by one of the `from_*` functions. pub fn as_slice(&self) -> &[u8] { &self.buf } /// Create a diff for a commit in mbox format for sending via email. pub fn from_diff( diff: &Diff<'_>, patch_idx: usize, patch_count: usize, commit_id: &Oid, summary: T, body: T, author: &Signature<'_>, opts: &mut EmailCreateOptions, ) -> Result { let buf = Buf::new(); let summary = summary.into_c_string()?; let body = body.into_c_string()?; unsafe { try_call!(raw::git_email_create_from_diff( buf.raw(), Binding::raw(diff), patch_idx, patch_count, Binding::raw(commit_id), summary.as_ptr(), body.as_ptr(), Binding::raw(author), opts.raw() )); Ok(Self { buf }) } } /// Create a diff for a commit in mbox format for sending via email. /// The commit must not be a merge commit. pub fn from_commit(commit: &Commit<'_>, opts: &mut EmailCreateOptions) -> Result { let buf = Buf::new(); unsafe { try_call!(raw::git_email_create_from_commit( buf.raw(), commit.raw(), opts.raw() )); Ok(Self { buf }) } } }