Skip to content

Commit 813dc4a

Browse files
committed
safe api
1 parent b09f24c commit 813dc4a

File tree

2 files changed

+114
-5
lines changed

2 files changed

+114
-5
lines changed

src/repo.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::diff::{
1212
binary_cb_c, file_cb_c, hunk_cb_c, line_cb_c, BinaryCb, DiffCallbacks, FileCb, HunkCb, LineCb,
1313
};
1414
use crate::oid_array::OidArray;
15-
use crate::stash::{stash_cb, StashApplyOptions, StashCbData};
15+
use crate::stash::{stash_cb, StashApplyOptions, StashCbData, StashSaveOptions};
1616
use crate::string_array::StringArray;
1717
use crate::tagforeach::{tag_foreach_cb, TagForeachCB, TagForeachData};
1818
use crate::util::{self, path_to_repo_path, Binding};
@@ -2844,6 +2844,25 @@ impl Repository {
28442844
}
28452845
}
28462846

2847+
///
2848+
pub fn stash_save_ext(
2849+
&mut self,
2850+
opts: Option<&mut StashSaveOptions<'_>>,
2851+
) -> Result<Oid, Error> {
2852+
unsafe {
2853+
let mut raw_oid = raw::git_oid {
2854+
id: [0; raw::GIT_OID_RAWSZ],
2855+
};
2856+
let opts = opts.map(|opts| opts.raw());
2857+
try_call!(raw::git_stash_save_with_opts(
2858+
&mut raw_oid,
2859+
self.raw(),
2860+
opts
2861+
));
2862+
Ok(Binding::from_raw(&raw_oid as *const _))
2863+
}
2864+
}
2865+
28472866
/// Apply a single stashed state from the stash list.
28482867
pub fn stash_apply(
28492868
&mut self,

src/stash.rs

Lines changed: 94 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,73 @@
11
use crate::build::CheckoutBuilder;
2-
use crate::util::Binding;
3-
use crate::{panic, raw, Oid, StashApplyProgress};
2+
use crate::util::{self, Binding};
3+
use crate::{panic, raw, IntoCString, Oid, Signature, StashApplyProgress, StashFlags};
44
use libc::{c_char, c_int, c_void, size_t};
5-
use std::ffi::CStr;
5+
use std::ffi::{c_uint, CStr, CString};
66
use std::mem;
77

8+
#[allow(unused)]
9+
/// Stash application options structure
10+
pub struct StashSaveOptions<'a> {
11+
message: Option<CString>,
12+
flags: Option<StashFlags>,
13+
stasher: Signature<'a>,
14+
pathspec: Vec<CString>,
15+
pathspec_ptrs: Vec<*const c_char>,
16+
raw_opts: raw::git_stash_save_options,
17+
}
18+
19+
impl<'a> StashSaveOptions<'a> {
20+
/// Creates a default
21+
pub fn new(stasher: Signature<'a>) -> Self {
22+
let mut opts = Self {
23+
message: None,
24+
flags: None,
25+
stasher,
26+
pathspec: Vec::new(),
27+
pathspec_ptrs: Vec::new(),
28+
raw_opts: unsafe { mem::zeroed() },
29+
};
30+
assert_eq!(
31+
unsafe {
32+
raw::git_stash_save_options_init(
33+
&mut opts.raw_opts,
34+
raw::GIT_STASH_SAVE_OPTIONS_VERSION,
35+
)
36+
},
37+
0
38+
);
39+
opts
40+
}
41+
42+
///
43+
pub fn flags(&mut self, flags: Option<StashFlags>) -> &mut Self {
44+
self.flags = flags;
45+
self
46+
}
47+
48+
/// Add to the array of paths patterns to build the stash.
49+
pub fn pathspec<T: IntoCString>(&mut self, pathspec: T) -> &mut Self {
50+
let s = util::cstring_to_repo_path(pathspec).unwrap();
51+
self.pathspec_ptrs.push(s.as_ptr());
52+
self.pathspec.push(s);
53+
self
54+
}
55+
56+
/// Acquire a pointer to the underlying raw options.
57+
///
58+
/// This function is unsafe as the pointer is only valid so long as this
59+
/// structure is not moved, modified, or used elsewhere.
60+
pub unsafe fn raw(&mut self) -> *const raw::git_stash_save_options {
61+
self.raw_opts.flags = self.flags.unwrap_or_else(StashFlags::empty).bits as c_uint;
62+
self.raw_opts.message = crate::call::convert(&self.message);
63+
self.raw_opts.paths.count = self.pathspec_ptrs.len() as size_t;
64+
self.raw_opts.paths.strings = self.pathspec_ptrs.as_ptr() as *mut _;
65+
self.raw_opts.stasher = self.stasher.raw();
66+
67+
&self.raw_opts as *const _
68+
}
69+
}
70+
871
/// Stash application progress notification function.
972
///
1073
/// Return `true` to continue processing, or `false` to
@@ -151,7 +214,7 @@ extern "C" fn stash_apply_progress_cb(
151214

152215
#[cfg(test)]
153216
mod tests {
154-
use crate::stash::StashApplyOptions;
217+
use crate::stash::{StashApplyOptions, StashSaveOptions};
155218
use crate::test::repo_init;
156219
use crate::{Repository, StashFlags, Status};
157220
use std::fs;
@@ -258,4 +321,31 @@ mod tests {
258321

259322
assert!(stash_name.starts_with("WIP on main:"));
260323
}
324+
325+
#[test]
326+
fn test_stash_save_ext() {
327+
let (_td, mut repo) = repo_init();
328+
let signature = repo.signature().unwrap();
329+
330+
let path_a = Path::new(repo.workdir().unwrap()).join("file_a");
331+
fs::File::create(&path_a)
332+
.unwrap()
333+
.write("data".as_bytes())
334+
.unwrap();
335+
336+
let path_b = Path::new(repo.workdir().unwrap()).join("file_b");
337+
fs::File::create(&path_b)
338+
.unwrap()
339+
.write("data".as_bytes())
340+
.unwrap();
341+
342+
assert_eq!(repo.statuses(None).unwrap().len(), 2);
343+
344+
let mut opt = StashSaveOptions::new(signature);
345+
opt.pathspec("file_a");
346+
opt.flags(Some(StashFlags::INCLUDE_UNTRACKED));
347+
repo.stash_save_ext(Some(&mut opt)).unwrap();
348+
349+
assert_eq!(repo.statuses(None).unwrap().len(), 1);
350+
}
261351
}

0 commit comments

Comments
 (0)