git2/
email.rs

1use std::ffi::CString;
2use std::{mem, ptr};
3
4use crate::util::Binding;
5use crate::{raw, Buf, Commit, DiffFindOptions, DiffOptions, Error, IntoCString};
6use crate::{Diff, Oid, Signature};
7
8/// A structure to represent patch in mbox format for sending via email
9pub struct Email {
10    buf: Buf,
11}
12
13/// Options for controlling the formatting of the generated e-mail.
14pub 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
21impl 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
44impl 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
133impl 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}