use std::c_str::CString;
use std::kinds::marker;
use std::mem;
use std::slice;
use libc::{c_void, c_int, c_char, c_uint};
use {raw, panic, Error, Cred, CredentialType, Oid};
pub struct RemoteCallbacks<'a> {
progress: Option<Box<TransferProgress<'a>>>,
credentials: Option<Box<Credentials<'a>>>,
sideband_progress: Option<Box<TransportMessage<'a>>>,
update_tips: Option<Box<UpdateTips<'a>>>,
}
pub struct Progress<'a> {
raw: ProgressState,
marker: marker::ContravariantLifetime<'a>,
}
enum ProgressState {
Borrowed(*const raw::git_transfer_progress),
Owned(raw::git_transfer_progress),
}
pub type Credentials<'a> = FnMut(&str, &str, CredentialType)
-> Result<Cred, Error> + 'a;
pub type TransferProgress<'a> = FnMut(Progress) -> bool + 'a;
pub type TransportMessage<'a> = FnMut(&[u8]) -> bool + 'a;
pub type UpdateTips<'a> = FnMut(&str, Oid, Oid) -> bool + 'a;
impl<'a> RemoteCallbacks<'a> {
pub fn new() -> RemoteCallbacks<'a> {
RemoteCallbacks {
credentials: None,
progress: None,
sideband_progress: None,
update_tips: None,
}
}
pub fn credentials<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
where F: FnMut(&str, &str, CredentialType)
-> Result<Cred, Error> + 'a
{
self.credentials = Some(box cb as Box<Credentials<'a>>);
self
}
pub fn transfer_progress<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
where F: FnMut(Progress) -> bool + 'a {
self.progress = Some(box cb as Box<TransferProgress<'a>>);
self
}
pub fn sideband_progress<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
where F: FnMut(&[u8]) -> bool + 'a {
self.sideband_progress = Some(box cb as Box<TransportMessage<'a>>);
self
}
pub fn update_tips<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
where F: FnMut(&str, Oid, Oid) -> bool + 'a {
self.update_tips = Some(box cb as Box<UpdateTips<'a>>);
self
}
pub unsafe fn raw(&mut self) -> raw::git_remote_callbacks {
let mut callbacks: raw::git_remote_callbacks = mem::zeroed();
assert_eq!(raw::git_remote_init_callbacks(&mut callbacks,
raw::GIT_REMOTE_CALLBACKS_VERSION), 0);
if self.progress.is_some() {
let f: raw::git_transfer_progress_cb = transfer_progress_cb;
callbacks.transfer_progress = Some(f);
}
if self.credentials.is_some() {
let f: raw::git_cred_acquire_cb = credentials_cb;
callbacks.credentials = Some(f);
}
if self.sideband_progress.is_some() {
let f: raw::git_transport_message_cb = sideband_progress_cb;
callbacks.sideband_progress = Some(f);
}
if self.update_tips.is_some() {
let f: extern fn(*const c_char, *const raw::git_oid,
*const raw::git_oid, *mut c_void) -> c_int
= update_tips_cb;
callbacks.update_tips = Some(f);
}
callbacks.payload = self as *mut _ as *mut _;
return callbacks;
}
}
impl<'a> Progress<'a> {
pub unsafe fn from_raw(raw: *const raw::git_transfer_progress)
-> Progress<'a> {
Progress {
raw: ProgressState::Borrowed(raw),
marker: marker::ContravariantLifetime,
}
}
pub fn total_objects(&self) -> uint {
unsafe { (*self.raw()).total_objects as uint }
}
pub fn indexed_objects(&self) -> uint {
unsafe { (*self.raw()).indexed_objects as uint }
}
pub fn received_objects(&self) -> uint {
unsafe { (*self.raw()).received_objects as uint }
}
pub fn local_objects(&self) -> uint {
unsafe { (*self.raw()).local_objects as uint }
}
pub fn total_deltas(&self) -> uint {
unsafe { (*self.raw()).total_deltas as uint }
}
pub fn indexed_deltas(&self) -> uint {
unsafe { (*self.raw()).indexed_deltas as uint }
}
pub fn received_bytes(&self) -> uint {
unsafe { (*self.raw()).received_bytes as uint }
}
pub fn to_owned(&self) -> Progress<'static> {
Progress {
raw: ProgressState::Owned(unsafe { *self.raw() }),
marker: marker::ContravariantLifetime,
}
}
fn raw(&self) -> *const raw::git_transfer_progress {
match self.raw {
ProgressState::Borrowed(raw) => raw,
ProgressState::Owned(ref raw) => raw as *const _,
}
}
}
extern fn credentials_cb(ret: *mut *mut raw::git_cred,
url: *const c_char,
username_from_url: *const c_char,
allowed_types: c_uint,
payload: *mut c_void) -> c_int {
unsafe {
let payload: &mut RemoteCallbacks = &mut *(payload as *mut RemoteCallbacks);
let callback = match payload.credentials {
Some(ref mut c) => c,
None => return raw::GIT_PASSTHROUGH as c_int,
};
*ret = 0 as *mut raw::git_cred;
let url = CString::new(url, false);
let url = match url.as_str() {
Some(url) => url,
None => return raw::GIT_PASSTHROUGH as c_int,
};
let username_from_url = if username_from_url.is_null() {
None
} else {
Some(CString::new(username_from_url, false))
};
let username_from_url = match username_from_url {
Some(ref username) => match username.as_str() {
Some(s) => Some(s),
None => return raw::GIT_PASSTHROUGH as c_int,
},
None => None,
};
let username_from_url = username_from_url.unwrap_or("");
let cred_type = CredentialType::from_bits_truncate(allowed_types as uint);
match panic::wrap(|| {
callback.call_mut((url, username_from_url, cred_type))
}) {
Some(Ok(cred)) => {
if allowed_types & (cred.credtype() as c_uint) != 0 {
*ret = cred.unwrap();
0
} else {
raw::GIT_PASSTHROUGH as c_int
}
}
Some(Err(e)) => e.raw_code() as c_int,
None => -1,
}
}
}
extern fn transfer_progress_cb(stats: *const raw::git_transfer_progress,
payload: *mut c_void) -> c_int {
unsafe {
let payload: &mut RemoteCallbacks = &mut *(payload as *mut RemoteCallbacks);
let callback = match payload.progress {
Some(ref mut c) => c,
None => return 0,
};
let progress = Progress::from_raw(stats);
let ok = panic::wrap(move || {
callback.call_mut((progress,))
}).unwrap_or(false);
if ok {0} else {-1}
}
}
extern fn sideband_progress_cb(str: *const c_char,
len: c_int,
payload: *mut c_void) -> c_int {
unsafe {
let payload: &mut RemoteCallbacks = &mut *(payload as *mut RemoteCallbacks);
let callback = match payload.sideband_progress {
Some(ref mut c) => c,
None => return 0,
};
let ptr = str as *const u8;
let buf = slice::from_raw_buf(&ptr, len as uint);
let ok = panic::wrap(|| {
callback.call_mut((buf,))
}).unwrap_or(false);
if ok {0} else {-1}
}
}
extern fn update_tips_cb(refname: *const c_char,
a: *const raw::git_oid,
b: *const raw::git_oid,
data: *mut c_void) -> c_int {
unsafe {
let payload: &mut RemoteCallbacks = &mut *(data as *mut RemoteCallbacks);
let callback = match payload.update_tips {
Some(ref mut c) => c,
None => return 0,
};
let refname = CString::new(refname, false);
let refname = refname.as_str().unwrap();
let a = Oid::from_raw(a);
let b = Oid::from_raw(b);
let ok = panic::wrap(|| {
callback.call_mut((refname, a, b))
}).unwrap_or(false);
if ok {0} else {-1}
}
}