1use libc::size_t;
2use std::iter::FusedIterator;
3use std::marker;
4use std::ops::Range;
5use std::str;
6
7use crate::util::Binding;
8use crate::{raw, signature, Error, Oid, Signature};
9
10pub struct Reflog {
12 raw: *mut raw::git_reflog,
13}
14
15pub struct ReflogEntry<'reflog> {
17 raw: *const raw::git_reflog_entry,
18 _marker: marker::PhantomData<&'reflog Reflog>,
19}
20
21pub struct ReflogIter<'reflog> {
23 range: Range<usize>,
24 reflog: &'reflog Reflog,
25}
26
27impl Reflog {
28 pub fn append(
30 &mut self,
31 new_oid: Oid,
32 committer: &Signature<'_>,
33 msg: Option<&str>,
34 ) -> Result<(), Error> {
35 let msg = crate::opt_cstr(msg)?;
36 unsafe {
37 try_call!(raw::git_reflog_append(
38 self.raw,
39 new_oid.raw(),
40 committer.raw(),
41 msg
42 ));
43 }
44 Ok(())
45 }
46
47 pub fn remove(&mut self, i: usize, rewrite_previous_entry: bool) -> Result<(), Error> {
54 unsafe {
55 try_call!(raw::git_reflog_drop(
56 self.raw,
57 i as size_t,
58 rewrite_previous_entry
59 ));
60 }
61 Ok(())
62 }
63
64 pub fn get(&self, i: usize) -> Option<ReflogEntry<'_>> {
69 unsafe {
70 let ptr = raw::git_reflog_entry_byindex(self.raw, i as size_t);
71 Binding::from_raw_opt(ptr)
72 }
73 }
74
75 pub fn len(&self) -> usize {
77 unsafe { raw::git_reflog_entrycount(self.raw) as usize }
78 }
79
80 pub fn is_empty(&self) -> bool {
82 self.len() == 0
83 }
84
85 pub fn iter(&self) -> ReflogIter<'_> {
87 ReflogIter {
88 range: 0..self.len(),
89 reflog: self,
90 }
91 }
92
93 pub fn write(&mut self) -> Result<(), Error> {
96 unsafe {
97 try_call!(raw::git_reflog_write(self.raw));
98 }
99 Ok(())
100 }
101}
102
103impl Binding for Reflog {
104 type Raw = *mut raw::git_reflog;
105
106 unsafe fn from_raw(raw: *mut raw::git_reflog) -> Reflog {
107 Reflog { raw }
108 }
109 fn raw(&self) -> *mut raw::git_reflog {
110 self.raw
111 }
112}
113
114impl Drop for Reflog {
115 fn drop(&mut self) {
116 unsafe { raw::git_reflog_free(self.raw) }
117 }
118}
119
120impl<'reflog> ReflogEntry<'reflog> {
121 pub fn committer(&self) -> Signature<'_> {
123 unsafe {
124 let ptr = raw::git_reflog_entry_committer(self.raw);
125 signature::from_raw_const(self, ptr)
126 }
127 }
128
129 pub fn id_new(&self) -> Oid {
131 unsafe { Binding::from_raw(raw::git_reflog_entry_id_new(self.raw)) }
132 }
133
134 pub fn id_old(&self) -> Oid {
136 unsafe { Binding::from_raw(raw::git_reflog_entry_id_old(self.raw)) }
137 }
138
139 pub fn message(&self) -> Option<&str> {
141 self.message_bytes().and_then(|s| str::from_utf8(s).ok())
142 }
143
144 pub fn message_bytes(&self) -> Option<&[u8]> {
146 unsafe { crate::opt_bytes(self, raw::git_reflog_entry_message(self.raw)) }
147 }
148}
149
150impl<'reflog> Binding for ReflogEntry<'reflog> {
151 type Raw = *const raw::git_reflog_entry;
152
153 unsafe fn from_raw(raw: *const raw::git_reflog_entry) -> ReflogEntry<'reflog> {
154 ReflogEntry {
155 raw,
156 _marker: marker::PhantomData,
157 }
158 }
159 fn raw(&self) -> *const raw::git_reflog_entry {
160 self.raw
161 }
162}
163
164impl<'reflog> Iterator for ReflogIter<'reflog> {
165 type Item = ReflogEntry<'reflog>;
166 fn next(&mut self) -> Option<ReflogEntry<'reflog>> {
167 self.range.next().and_then(|i| self.reflog.get(i))
168 }
169 fn size_hint(&self) -> (usize, Option<usize>) {
170 self.range.size_hint()
171 }
172}
173impl<'reflog> DoubleEndedIterator for ReflogIter<'reflog> {
174 fn next_back(&mut self) -> Option<ReflogEntry<'reflog>> {
175 self.range.next_back().and_then(|i| self.reflog.get(i))
176 }
177}
178impl<'reflog> FusedIterator for ReflogIter<'reflog> {}
179impl<'reflog> ExactSizeIterator for ReflogIter<'reflog> {}
180
181#[cfg(test)]
182mod tests {
183 #[test]
184 fn smoke() {
185 let (_td, repo) = crate::test::repo_init();
186 let mut reflog = repo.reflog("HEAD").unwrap();
187 assert_eq!(reflog.iter().len(), 1);
188 reflog.write().unwrap();
189
190 let entry = reflog.iter().next().unwrap();
191 assert!(entry.message().is_some());
192
193 repo.reflog_rename("HEAD", "refs/heads/foo").unwrap();
194 repo.reflog_delete("refs/heads/foo").unwrap();
195 }
196}