use std::ffi::{self, CString};
use std::io;
use std::mem;
use std::slice;
use std::str;
use std::sync::{StaticMutex, MUTEX_INIT};
use libc::{c_int, c_void, c_uint, c_char, size_t};
use {raw, panic, Error, Remote};
use util::Binding;
#[allow(missing_copy_implementations)]
pub struct Transport {
raw: *mut raw::git_transport,
owned: bool,
}
pub trait SmartSubtransport: Send {
fn action(&self, url: &str, action: Service)
-> Result<Box<SmartSubtransportStream>, Error>;
fn close(&self) -> Result<(), Error>;
}
#[derive(Copy)]
#[allow(missing_docs)]
pub enum Service {
UploadPackLs,
UploadPack,
ReceivePackLs,
ReceivePack,
}
pub trait SmartSubtransportStream: Reader + Writer + Send {}
impl<T: Reader + Writer + Send> SmartSubtransportStream for T {}
type TransportFactory = Fn(&Remote) -> Result<Transport, Error> + Send + Sync;
struct TransportData {
factory: Box<TransportFactory>,
}
#[repr(C)]
struct RawSmartSubtransport {
raw: raw::git_smart_subtransport,
obj: Box<SmartSubtransport>,
}
#[repr(C)]
struct RawSmartSubtransportStream {
raw: raw::git_smart_subtransport_stream,
obj: Box<SmartSubtransportStream>,
}
pub unsafe fn register<F>(prefix: &str, factory: F) -> Result<(), Error>
where F: Fn(&Remote) -> Result<Transport, Error> + Send + Sync
{
let mut data = Box::new(TransportData {
factory: Box::new(factory),
});
let prefix = CString::from_slice(prefix.as_bytes());
let datap = (&mut *data) as *mut TransportData as *mut c_void;
try_call!(raw::git_transport_register(prefix,
transport_factory,
datap));
mem::forget(data);
Ok(())
}
impl Transport {
pub fn smart<S>(remote: &Remote,
rpc: bool,
subtransport: S) -> Result<Transport, Error>
where S: SmartSubtransport
{
static LOCK: StaticMutex = MUTEX_INIT;
static mut PTR: usize = 0;
let mut defn = raw::git_smart_subtransport_definition {
callback: smart_factory,
rpc: rpc as c_uint,
};
let mut ret = 0 as *mut _;
let raw = Box::new(RawSmartSubtransport {
raw: raw::git_smart_subtransport {
action: subtransport_action,
close: subtransport_close,
free: subtransport_free,
},
obj: Box::new(subtransport),
});
unsafe {
let _g = LOCK.lock();
assert!(PTR == 0);
PTR = &*raw as *const RawSmartSubtransport as usize;
try_call!(raw::git_transport_smart(&mut ret, remote.raw(),
&mut defn as *mut _ as *mut _));
mem::forget(raw); PTR = 0;
}
return Ok(Transport { raw: ret, owned: true });
extern fn smart_factory(out: *mut *mut raw::git_smart_subtransport,
_owner: *mut raw::git_transport) -> c_int {
unsafe {
assert!(PTR as usize != 0);
*out = PTR as *mut raw::git_smart_subtransport;
0
}
}
}
}
impl Drop for Transport {
fn drop(&mut self) {
if self.owned {
unsafe {
((*self.raw).free)(self.raw)
}
}
}
}
extern fn transport_factory(out: *mut *mut raw::git_transport,
owner: *mut raw::git_remote,
param: *mut c_void) -> c_int {
unsafe {
let remote = Binding::from_raw(owner);
let data = &mut *(param as *mut TransportData);
let res = panic::wrap(|| (data.factory)(&remote));
mem::forget(remote); let mut transport = match res {
Some(Ok(transport)) => transport,
Some(Err(e)) => return e.raw_code() as c_int,
None => return -1,
};
*out = transport.raw;
transport.owned = false;
0
}
}
extern fn subtransport_action(stream: *mut *mut raw::git_smart_subtransport_stream,
raw_transport: *mut raw::git_smart_subtransport,
url: *const c_char,
action: raw::git_smart_service_t) -> c_int {
unsafe {
let url = ffi::c_str_to_bytes(&url);
let url = match str::from_utf8(url).ok() {
Some(s) => s,
None => return -1,
};
let action = match action {
raw::GIT_SERVICE_UPLOADPACK_LS => Service::UploadPackLs,
raw::GIT_SERVICE_UPLOADPACK => Service::UploadPack,
raw::GIT_SERVICE_RECEIVEPACK_LS => Service::ReceivePackLs,
raw::GIT_SERVICE_RECEIVEPACK => Service::ReceivePack,
};
let transport = &mut *(raw_transport as *mut RawSmartSubtransport);
let obj = match panic::wrap(|| transport.obj.action(url, action)) {
Some(Ok(s)) => s,
Some(Err(e)) => return e.raw_code() as c_int,
None => return -1,
};
*stream = mem::transmute(Box::new(RawSmartSubtransportStream {
raw: raw::git_smart_subtransport_stream {
subtransport: raw_transport,
read: stream_read,
write: stream_write,
free: stream_free,
},
obj: obj,
}));
0
}
}
extern fn subtransport_close(transport: *mut raw::git_smart_subtransport)
-> c_int {
unsafe {
let transport = &mut *(transport as *mut RawSmartSubtransport);
match panic::wrap(|| transport.obj.close()) {
Some(Ok(())) => 0,
Some(Err(e)) => e.raw_code() as c_int,
None => -1,
}
}
}
extern fn subtransport_free(transport: *mut raw::git_smart_subtransport) {
unsafe {
mem::transmute::<_, Box<RawSmartSubtransport>>(transport);
}
}
extern fn stream_read(stream: *mut raw::git_smart_subtransport_stream,
buffer: *mut c_char,
buf_size: size_t,
bytes_read: *mut size_t) -> c_int {
unsafe {
let transport = &mut *(stream as *mut RawSmartSubtransportStream);
let buffer = buffer as *mut u8;
let buf = slice::from_raw_mut_buf(&buffer, buf_size as usize);
match panic::wrap(|| transport.obj.read(buf)) {
Some(Ok(n)) => { *bytes_read = n as size_t; 0 }
Some(Err(ref e)) if e.kind == io::EndOfFile => {
*bytes_read = 0; 0
}
Some(Err(..)) => -2, None => -1,
}
}
}
extern fn stream_write(stream: *mut raw::git_smart_subtransport_stream,
buffer: *const c_char,
len: size_t) -> c_int {
unsafe {
let transport = &mut *(stream as *mut RawSmartSubtransportStream);
let buffer = buffer as *const u8;
let buf = slice::from_raw_buf(&buffer, len as usize);
match panic::wrap(|| transport.obj.write(buf)) {
Some(Ok(())) => 0,
Some(Err(..)) => -2, None => -1,
}
}
}
extern fn stream_free(stream: *mut raw::git_smart_subtransport_stream) {
unsafe {
mem::transmute::<_, Box<RawSmartSubtransportStream>>(stream);
}
}