use libc::size_t;
use std::iter::FusedIterator;
use std::marker;
use std::ops::Range;
use std::str;
use crate::util::Binding;
use crate::{raw, signature, Error, Oid, Signature};
pub struct Reflog {
raw: *mut raw::git_reflog,
}
pub struct ReflogEntry<'reflog> {
raw: *const raw::git_reflog_entry,
_marker: marker::PhantomData<&'reflog Reflog>,
}
pub struct ReflogIter<'reflog> {
range: Range<usize>,
reflog: &'reflog Reflog,
}
impl Reflog {
pub fn append(
&mut self,
new_oid: Oid,
committer: &Signature<'_>,
msg: Option<&str>,
) -> Result<(), Error> {
let msg = crate::opt_cstr(msg)?;
unsafe {
try_call!(raw::git_reflog_append(
self.raw,
new_oid.raw(),
committer.raw(),
msg
));
}
Ok(())
}
pub fn remove(&mut self, i: usize, rewrite_previous_entry: bool) -> Result<(), Error> {
unsafe {
try_call!(raw::git_reflog_drop(
self.raw,
i as size_t,
rewrite_previous_entry
));
}
Ok(())
}
pub fn get(&self, i: usize) -> Option<ReflogEntry<'_>> {
unsafe {
let ptr = raw::git_reflog_entry_byindex(self.raw, i as size_t);
Binding::from_raw_opt(ptr)
}
}
pub fn len(&self) -> usize {
unsafe { raw::git_reflog_entrycount(self.raw) as usize }
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn iter(&self) -> ReflogIter<'_> {
ReflogIter {
range: 0..self.len(),
reflog: self,
}
}
pub fn write(&mut self) -> Result<(), Error> {
unsafe {
try_call!(raw::git_reflog_write(self.raw));
}
Ok(())
}
}
impl Binding for Reflog {
type Raw = *mut raw::git_reflog;
unsafe fn from_raw(raw: *mut raw::git_reflog) -> Reflog {
Reflog { raw }
}
fn raw(&self) -> *mut raw::git_reflog {
self.raw
}
}
impl Drop for Reflog {
fn drop(&mut self) {
unsafe { raw::git_reflog_free(self.raw) }
}
}
impl<'reflog> ReflogEntry<'reflog> {
pub fn committer(&self) -> Signature<'_> {
unsafe {
let ptr = raw::git_reflog_entry_committer(self.raw);
signature::from_raw_const(self, ptr)
}
}
pub fn id_new(&self) -> Oid {
unsafe { Binding::from_raw(raw::git_reflog_entry_id_new(self.raw)) }
}
pub fn id_old(&self) -> Oid {
unsafe { Binding::from_raw(raw::git_reflog_entry_id_old(self.raw)) }
}
pub fn message(&self) -> Option<&str> {
self.message_bytes().and_then(|s| str::from_utf8(s).ok())
}
pub fn message_bytes(&self) -> Option<&[u8]> {
unsafe { crate::opt_bytes(self, raw::git_reflog_entry_message(self.raw)) }
}
}
impl<'reflog> Binding for ReflogEntry<'reflog> {
type Raw = *const raw::git_reflog_entry;
unsafe fn from_raw(raw: *const raw::git_reflog_entry) -> ReflogEntry<'reflog> {
ReflogEntry {
raw,
_marker: marker::PhantomData,
}
}
fn raw(&self) -> *const raw::git_reflog_entry {
self.raw
}
}
impl<'reflog> Iterator for ReflogIter<'reflog> {
type Item = ReflogEntry<'reflog>;
fn next(&mut self) -> Option<ReflogEntry<'reflog>> {
self.range.next().and_then(|i| self.reflog.get(i))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.range.size_hint()
}
}
impl<'reflog> DoubleEndedIterator for ReflogIter<'reflog> {
fn next_back(&mut self) -> Option<ReflogEntry<'reflog>> {
self.range.next_back().and_then(|i| self.reflog.get(i))
}
}
impl<'reflog> FusedIterator for ReflogIter<'reflog> {}
impl<'reflog> ExactSizeIterator for ReflogIter<'reflog> {}
#[cfg(test)]
mod tests {
#[test]
fn smoke() {
let (_td, repo) = crate::test::repo_init();
let mut reflog = repo.reflog("HEAD").unwrap();
assert_eq!(reflog.iter().len(), 1);
reflog.write().unwrap();
let entry = reflog.iter().next().unwrap();
assert!(entry.message().is_some());
repo.reflog_rename("HEAD", "refs/heads/foo").unwrap();
repo.reflog_delete("refs/heads/foo").unwrap();
}
}