use crate::ffi::error::catch_panic;
use crate::ffi::error::get_error_msg;
use crate::ffi::error::Error;
use crate::ffi::status::Status;
use crate::ffi::util::check_null;
use crate::ffi::util::write_to_c_buf;
use crate::GitOid;
use crate::HashAlgorithm;
use crate::ObjectType;
use std::ffi::c_char;
use std::ffi::c_int;
use std::ffi::CStr;
use std::ffi::CString;
use std::fs::File;
use std::io::BufReader;
use std::os::unix::prelude::FromRawFd;
use std::os::unix::prelude::RawFd;
use std::ptr::null;
use std::ptr::null_mut;
use std::slice;
use url::Url;
#[no_mangle]
pub extern "C" fn gitoid_get_error_message(buffer: *mut c_char, length: c_int) -> c_int {
if buffer.is_null() {
return Status::BufferIsNull as c_int;
}
let buffer = unsafe { slice::from_raw_parts_mut(buffer as *mut u8, length as usize) };
let last_err = get_error_msg().unwrap_or_default();
write_to_c_buf(&last_err, buffer)
}
#[no_mangle]
pub extern "C" fn gitoid_invalid(gitoid: *const GitOid) -> c_int {
let output = catch_panic(|| {
let gitoid = unsafe { &*gitoid };
let result = gitoid.hash_len() == 0;
Ok(result as c_int)
});
output.unwrap_or(Status::UnexpectedError as c_int)
}
#[no_mangle]
pub extern "C" fn gitoid_new_from_bytes(
hash_algorithm: HashAlgorithm,
object_type: ObjectType,
content: *const u8,
content_len: usize,
) -> GitOid {
let output = catch_panic(|| {
check_null(content, Error::ContentPtrIsNull)?;
let content = unsafe { slice::from_raw_parts(content, content_len) };
Ok(GitOid::new_from_bytes(hash_algorithm, object_type, content))
});
output.unwrap_or_else(GitOid::new_invalid)
}
#[no_mangle]
pub extern "C" fn gitoid_new_from_str(
hash_algorithm: HashAlgorithm,
object_type: ObjectType,
s: *const c_char,
) -> GitOid {
let output = catch_panic(|| {
check_null(s, Error::StringPtrIsNull)?;
let s = unsafe { CStr::from_ptr(s) }.to_str()?;
Ok(GitOid::new_from_str(hash_algorithm, object_type, s))
});
output.unwrap_or_else(GitOid::new_invalid)
}
#[no_mangle]
pub extern "C" fn gitoid_new_from_url(s: *const c_char) -> GitOid {
let output = catch_panic(|| {
check_null(s, Error::StringPtrIsNull)?;
let raw_url = unsafe { CStr::from_ptr(s) }.to_str()?;
let url = Url::parse(raw_url)?;
let gitoid = GitOid::new_from_url(url)?;
Ok(gitoid)
});
output.unwrap_or_else(GitOid::new_invalid)
}
#[cfg(target_family = "unix")]
#[no_mangle]
pub extern "C" fn gitoid_new_from_reader(
hash_algorithm: HashAlgorithm,
object_type: ObjectType,
fd: RawFd,
) -> GitOid {
let output = catch_panic(|| {
let file = unsafe { File::from_raw_fd(fd) };
let reader = BufReader::new(file);
let gitoid = GitOid::new_from_reader(hash_algorithm, object_type, reader)?;
Ok(gitoid)
});
output.unwrap_or_else(GitOid::new_invalid)
}
#[cfg(target_family = "windows")]
#[no_mangle]
pub extern "C" fn gitoid_new_from_reader(
hash_algorithm: HashAlgorithm,
object_type: ObjectType,
handle: RawHandle,
) -> GitOid {
let output = catch_panic(|| {
let file = unsafe { File::from_raw_handle(handle) };
let reader = BufReader::new(file);
let gitoid = GitOid::new_from_reader(hash_algorithm, object_type, reader)?;
Ok(gitoid)
});
output.unwrap_or_else(GitOid::new_invalid)
}
#[no_mangle]
pub extern "C" fn gitoid_get_url(ptr: *mut GitOid) -> *mut c_char {
let output = catch_panic(|| {
check_null(ptr, Error::GitOidPtrIsNull)?;
let gitoid = unsafe { &mut *ptr };
let url = CString::new(gitoid.url().as_str())?;
Ok(url.into_raw())
});
output.unwrap_or_else(null_mut)
}
#[no_mangle]
pub extern "C" fn gitoid_get_hash_bytes(ptr: *mut GitOid) -> *const u8 {
let output = catch_panic(|| {
let gitoid = unsafe { &*ptr };
let hash = gitoid.hash();
Ok(hash.as_ptr())
});
output.unwrap_or_else(null)
}
#[no_mangle]
pub extern "C" fn gitoid_get_hash_string(ptr: *mut GitOid) -> *mut c_char {
let output = catch_panic(|| {
let gitoid = unsafe { &*ptr };
let hash = gitoid.hash();
let hash_str = hash.as_hex();
let hash_c_str = CString::new(hash_str)?;
Ok(hash_c_str.into_raw())
});
output.unwrap_or_else(null_mut)
}
macro_rules! embed_cstr {
($name:ident, $arr:expr) => {
const $name: *const c_char = $arr.as_ptr();
};
}
#[no_mangle]
pub extern "C" fn gitoid_hash_algorithm_name(hash_algorithm: HashAlgorithm) -> *const c_char {
embed_cstr!(HASH_ALGORITHM_SHA1, [0x73, 0x68, 0x61, 0x31, 0x00]);
embed_cstr!(
HASH_ALGORITHM_SHA256,
[0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x00]
);
let output = catch_panic(|| {
Ok(match hash_algorithm {
HashAlgorithm::Sha1 => HASH_ALGORITHM_SHA1,
HashAlgorithm::Sha256 => HASH_ALGORITHM_SHA256,
})
});
output.unwrap_or_else(null)
}
#[no_mangle]
pub extern "C" fn gitoid_object_type_name(object_type: ObjectType) -> *const c_char {
embed_cstr!(OBJECT_TYPE_BLOB, [0x62, 0x6C, 0x6F, 0x62, 0x00]);
embed_cstr!(OBJECT_TYPE_TREE, [0x74, 0x72, 0x65, 0x65, 0x00]);
embed_cstr!(
OBJECT_TYPE_COMMIT,
[0x63, 0x6F, 0x6D, 0x6D, 0x69, 0x74, 0x00]
);
embed_cstr!(OBJECT_TYPE_TAG, [0x74, 0x61, 0x67, 0x00]);
let output = catch_panic(|| {
Ok(match object_type {
ObjectType::Blob => OBJECT_TYPE_BLOB,
ObjectType::Tree => OBJECT_TYPE_TREE,
ObjectType::Commit => OBJECT_TYPE_COMMIT,
ObjectType::Tag => OBJECT_TYPE_TAG,
})
});
output.unwrap_or_else(null)
}
#[no_mangle]
pub extern "C" fn gitoid_str_free(s: *mut c_char) {
if s.is_null() {
return;
}
unsafe { CString::from_raw(s) };
}