1use libc::{c_uint, c_ushort};
2use std::ffi::CString;
3use std::marker;
4use std::mem;
5use std::ptr;
6use std::str;
7
8use crate::call::Convert;
9use crate::util::Binding;
10use crate::IntoCString;
11use crate::{raw, Commit, FileFavor, Oid};
12
13pub struct AnnotatedCommit<'repo> {
19 raw: *mut raw::git_annotated_commit,
20 _marker: marker::PhantomData<Commit<'repo>>,
21}
22
23pub struct MergeOptions {
25 raw: raw::git_merge_options,
26}
27
28pub struct MergeFileOptions {
30 ancestor_label: Option<CString>,
31 our_label: Option<CString>,
32 their_label: Option<CString>,
33 raw: raw::git_merge_file_options,
34}
35
36pub struct MergeFileResult {
38 raw: raw::git_merge_file_result,
39}
40
41impl<'repo> AnnotatedCommit<'repo> {
42 pub fn id(&self) -> Oid {
44 unsafe { Binding::from_raw(raw::git_annotated_commit_id(self.raw)) }
45 }
46
47 pub fn refname(&self) -> Option<&str> {
51 str::from_utf8(self.refname_bytes()).ok()
52 }
53
54 pub fn refname_bytes(&self) -> &[u8] {
56 unsafe { crate::opt_bytes(self, raw::git_annotated_commit_ref(&*self.raw)).unwrap() }
57 }
58}
59
60impl Default for MergeOptions {
61 fn default() -> Self {
62 Self::new()
63 }
64}
65
66impl MergeOptions {
67 pub fn new() -> MergeOptions {
69 let mut opts = MergeOptions {
70 raw: unsafe { mem::zeroed() },
71 };
72 assert_eq!(unsafe { raw::git_merge_init_options(&mut opts.raw, 1) }, 0);
73 opts
74 }
75
76 fn flag(&mut self, opt: u32, val: bool) -> &mut MergeOptions {
77 if val {
78 self.raw.flags |= opt;
79 } else {
80 self.raw.flags &= !opt;
81 }
82 self
83 }
84
85 pub fn find_renames(&mut self, find: bool) -> &mut MergeOptions {
87 self.flag(raw::GIT_MERGE_FIND_RENAMES as u32, find)
88 }
89
90 pub fn fail_on_conflict(&mut self, fail: bool) -> &mut MergeOptions {
93 self.flag(raw::GIT_MERGE_FAIL_ON_CONFLICT as u32, fail)
94 }
95
96 pub fn skip_reuc(&mut self, skip: bool) -> &mut MergeOptions {
98 self.flag(raw::GIT_MERGE_FAIL_ON_CONFLICT as u32, skip)
99 }
100
101 pub fn no_recursive(&mut self, disable: bool) -> &mut MergeOptions {
105 self.flag(raw::GIT_MERGE_NO_RECURSIVE as u32, disable)
106 }
107
108 pub fn rename_threshold(&mut self, thresh: u32) -> &mut MergeOptions {
110 self.raw.rename_threshold = thresh;
111 self
112 }
113
114 pub fn target_limit(&mut self, limit: u32) -> &mut MergeOptions {
119 self.raw.target_limit = limit as c_uint;
120 self
121 }
122
123 pub fn recursion_limit(&mut self, limit: u32) -> &mut MergeOptions {
128 self.raw.recursion_limit = limit as c_uint;
129 self
130 }
131
132 pub fn file_favor(&mut self, favor: FileFavor) -> &mut MergeOptions {
134 self.raw.file_favor = favor.convert();
135 self
136 }
137
138 fn file_flag(&mut self, opt: u32, val: bool) -> &mut MergeOptions {
139 if val {
140 self.raw.file_flags |= opt;
141 } else {
142 self.raw.file_flags &= !opt;
143 }
144 self
145 }
146
147 pub fn standard_style(&mut self, standard: bool) -> &mut MergeOptions {
149 self.file_flag(raw::GIT_MERGE_FILE_STYLE_MERGE as u32, standard)
150 }
151
152 pub fn diff3_style(&mut self, diff3: bool) -> &mut MergeOptions {
154 self.file_flag(raw::GIT_MERGE_FILE_STYLE_DIFF3 as u32, diff3)
155 }
156
157 pub fn simplify_alnum(&mut self, simplify: bool) -> &mut MergeOptions {
159 self.file_flag(raw::GIT_MERGE_FILE_SIMPLIFY_ALNUM as u32, simplify)
160 }
161
162 pub fn ignore_whitespace(&mut self, ignore: bool) -> &mut MergeOptions {
164 self.file_flag(raw::GIT_MERGE_FILE_IGNORE_WHITESPACE as u32, ignore)
165 }
166
167 pub fn ignore_whitespace_change(&mut self, ignore: bool) -> &mut MergeOptions {
169 self.file_flag(raw::GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE as u32, ignore)
170 }
171
172 pub fn ignore_whitespace_eol(&mut self, ignore: bool) -> &mut MergeOptions {
174 self.file_flag(raw::GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL as u32, ignore)
175 }
176
177 pub fn patience(&mut self, patience: bool) -> &mut MergeOptions {
179 self.file_flag(raw::GIT_MERGE_FILE_DIFF_PATIENCE as u32, patience)
180 }
181
182 pub fn minimal(&mut self, minimal: bool) -> &mut MergeOptions {
184 self.file_flag(raw::GIT_MERGE_FILE_DIFF_MINIMAL as u32, minimal)
185 }
186
187 pub unsafe fn raw(&self) -> *const raw::git_merge_options {
189 &self.raw as *const _
190 }
191}
192
193impl<'repo> Binding for AnnotatedCommit<'repo> {
194 type Raw = *mut raw::git_annotated_commit;
195 unsafe fn from_raw(raw: *mut raw::git_annotated_commit) -> AnnotatedCommit<'repo> {
196 AnnotatedCommit {
197 raw,
198 _marker: marker::PhantomData,
199 }
200 }
201 fn raw(&self) -> *mut raw::git_annotated_commit {
202 self.raw
203 }
204}
205
206impl<'repo> Drop for AnnotatedCommit<'repo> {
207 fn drop(&mut self) {
208 unsafe { raw::git_annotated_commit_free(self.raw) }
209 }
210}
211
212impl Default for MergeFileOptions {
213 fn default() -> Self {
214 Self::new()
215 }
216}
217
218impl MergeFileOptions {
219 pub fn new() -> MergeFileOptions {
221 let mut opts = MergeFileOptions {
222 ancestor_label: None,
223 our_label: None,
224 their_label: None,
225 raw: unsafe { mem::zeroed() },
226 };
227 assert_eq!(
228 unsafe { raw::git_merge_file_options_init(&mut opts.raw, 1) },
229 0
230 );
231 opts
232 }
233
234 pub fn ancestor_label<T: IntoCString>(&mut self, t: T) -> &mut MergeFileOptions {
237 self.ancestor_label = Some(t.into_c_string().unwrap());
238
239 self.raw.ancestor_label = self
240 .ancestor_label
241 .as_ref()
242 .map(|s| s.as_ptr())
243 .unwrap_or(ptr::null());
244
245 self
246 }
247
248 pub fn our_label<T: IntoCString>(&mut self, t: T) -> &mut MergeFileOptions {
251 self.our_label = Some(t.into_c_string().unwrap());
252
253 self.raw.our_label = self
254 .our_label
255 .as_ref()
256 .map(|s| s.as_ptr())
257 .unwrap_or(ptr::null());
258
259 self
260 }
261
262 pub fn their_label<T: IntoCString>(&mut self, t: T) -> &mut MergeFileOptions {
265 self.their_label = Some(t.into_c_string().unwrap());
266
267 self.raw.their_label = self
268 .their_label
269 .as_ref()
270 .map(|s| s.as_ptr())
271 .unwrap_or(ptr::null());
272
273 self
274 }
275
276 pub fn favor(&mut self, favor: FileFavor) -> &mut MergeFileOptions {
278 self.raw.favor = favor.convert();
279 self
280 }
281
282 fn flag(&mut self, opt: raw::git_merge_file_flag_t, val: bool) -> &mut MergeFileOptions {
283 if val {
284 self.raw.flags |= opt as u32;
285 } else {
286 self.raw.flags &= !opt as u32;
287 }
288 self
289 }
290
291 pub fn style_standard(&mut self, standard: bool) -> &mut MergeFileOptions {
293 self.flag(raw::GIT_MERGE_FILE_STYLE_MERGE, standard)
294 }
295
296 pub fn style_diff3(&mut self, diff3: bool) -> &mut MergeFileOptions {
298 self.flag(raw::GIT_MERGE_FILE_STYLE_DIFF3, diff3)
299 }
300
301 pub fn simplify_alnum(&mut self, simplify: bool) -> &mut MergeFileOptions {
303 self.flag(raw::GIT_MERGE_FILE_SIMPLIFY_ALNUM, simplify)
304 }
305
306 pub fn ignore_whitespace(&mut self, ignore: bool) -> &mut MergeFileOptions {
308 self.flag(raw::GIT_MERGE_FILE_IGNORE_WHITESPACE, ignore)
309 }
310
311 pub fn ignore_whitespace_change(&mut self, ignore: bool) -> &mut MergeFileOptions {
313 self.flag(raw::GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE, ignore)
314 }
315
316 pub fn ignore_whitespace_eol(&mut self, ignore: bool) -> &mut MergeFileOptions {
318 self.flag(raw::GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL, ignore)
319 }
320
321 pub fn patience(&mut self, patience: bool) -> &mut MergeFileOptions {
323 self.flag(raw::GIT_MERGE_FILE_DIFF_PATIENCE, patience)
324 }
325
326 pub fn minimal(&mut self, minimal: bool) -> &mut MergeFileOptions {
328 self.flag(raw::GIT_MERGE_FILE_DIFF_MINIMAL, minimal)
329 }
330
331 pub fn style_zdiff3(&mut self, zdiff3: bool) -> &mut MergeFileOptions {
333 self.flag(raw::GIT_MERGE_FILE_STYLE_ZDIFF3, zdiff3)
334 }
335
336 pub fn accept_conflicts(&mut self, accept: bool) -> &mut MergeFileOptions {
338 self.flag(raw::GIT_MERGE_FILE_ACCEPT_CONFLICTS, accept)
339 }
340
341 pub fn marker_size(&mut self, size: u16) -> &mut MergeFileOptions {
343 self.raw.marker_size = size as c_ushort;
344 self
345 }
346
347 pub(crate) unsafe fn raw(&mut self) -> *const raw::git_merge_file_options {
352 &self.raw
353 }
354}
355
356impl MergeFileResult {
357 pub fn is_automergeable(&self) -> bool {
360 self.raw.automergeable > 0
361 }
362
363 pub fn path(&self) -> Option<&str> {
368 self.path_bytes()
369 .and_then(|bytes| str::from_utf8(bytes).ok())
370 }
371
372 pub fn path_bytes(&self) -> Option<&[u8]> {
374 unsafe { crate::opt_bytes(self, self.raw.path) }
375 }
376
377 pub fn mode(&self) -> u32 {
379 self.raw.mode as u32
380 }
381
382 pub fn content(&self) -> &[u8] {
384 unsafe { std::slice::from_raw_parts(self.raw.ptr as *const u8, self.raw.len as usize) }
385 }
386}
387
388impl Binding for MergeFileResult {
389 type Raw = raw::git_merge_file_result;
390 unsafe fn from_raw(raw: raw::git_merge_file_result) -> MergeFileResult {
391 MergeFileResult { raw }
392 }
393 fn raw(&self) -> raw::git_merge_file_result {
394 unimplemented!()
395 }
396}
397
398impl Drop for MergeFileResult {
399 fn drop(&mut self) {
400 unsafe { raw::git_merge_file_result_free(&mut self.raw) }
401 }
402}
403
404impl std::fmt::Debug for MergeFileResult {
405 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
406 let mut ds = f.debug_struct("MergeFileResult");
407 if let Some(path) = &self.path() {
408 ds.field("path", path);
409 }
410 ds.field("automergeable", &self.is_automergeable());
411 ds.field("mode", &self.mode());
412 ds.finish()
413 }
414}