use crate::{with_tmp_pool, Error};
pub fn initialize(assume_native_utf8: bool) {
with_tmp_pool(|pool| unsafe {
subversion_sys::svn_utf_initialize2(
if assume_native_utf8 { 1 } else { 0 },
pool.as_mut_ptr(),
);
})
}
pub fn cstring_to_utf8(src: &str) -> Result<String, Error<'static>> {
with_tmp_pool(|pool| unsafe {
let src_cstr = std::ffi::CString::new(src)?;
let mut dest_ptr: *const std::ffi::c_char = std::ptr::null();
let err = subversion_sys::svn_utf_cstring_to_utf8(
&mut dest_ptr,
src_cstr.as_ptr(),
pool.as_mut_ptr(),
);
Error::from_raw(err)?;
if dest_ptr.is_null() {
return Err(Error::from_message("UTF-8 conversion returned null"));
}
let dest_cstr = std::ffi::CStr::from_ptr(dest_ptr);
Ok(dest_cstr.to_str()?.to_string())
})
}
pub fn cstring_to_utf8_ex(src: &str, frompage: &str) -> Result<String, Error<'static>> {
with_tmp_pool(|pool| unsafe {
let src_cstr = std::ffi::CString::new(src)?;
let frompage_cstr = std::ffi::CString::new(frompage)?;
let mut dest_ptr: *const std::ffi::c_char = std::ptr::null();
let err = subversion_sys::svn_utf_cstring_to_utf8_ex2(
&mut dest_ptr,
src_cstr.as_ptr(),
frompage_cstr.as_ptr(),
pool.as_mut_ptr(),
);
Error::from_raw(err)?;
if dest_ptr.is_null() {
return Err(Error::from_message("UTF-8 conversion returned null"));
}
let dest_cstr = std::ffi::CStr::from_ptr(dest_ptr);
Ok(dest_cstr.to_str()?.to_string())
})
}
pub fn cstring_from_utf8(src: &str) -> Result<String, Error<'static>> {
with_tmp_pool(|pool| unsafe {
let src_cstr = std::ffi::CString::new(src)?;
let mut dest_ptr: *const std::ffi::c_char = std::ptr::null();
let err = subversion_sys::svn_utf_cstring_from_utf8(
&mut dest_ptr,
src_cstr.as_ptr(),
pool.as_mut_ptr(),
);
Error::from_raw(err)?;
if dest_ptr.is_null() {
return Err(Error::from_message(
"Native encoding conversion returned null",
));
}
let dest_cstr = std::ffi::CStr::from_ptr(dest_ptr);
Ok(dest_cstr.to_str()?.to_string())
})
}
pub fn cstring_from_utf8_ex(src: &str, topage: &str) -> Result<String, Error<'static>> {
with_tmp_pool(|pool| unsafe {
let src_cstr = std::ffi::CString::new(src)?;
let topage_cstr = std::ffi::CString::new(topage)?;
let mut dest_ptr: *const std::ffi::c_char = std::ptr::null();
let err = subversion_sys::svn_utf_cstring_from_utf8_ex2(
&mut dest_ptr,
src_cstr.as_ptr(),
topage_cstr.as_ptr(),
pool.as_mut_ptr(),
);
Error::from_raw(err)?;
if dest_ptr.is_null() {
return Err(Error::from_message(
"Target encoding conversion returned null",
));
}
let dest_cstr = std::ffi::CStr::from_ptr(dest_ptr);
Ok(dest_cstr.to_str()?.to_string())
})
}
pub fn cstring_from_utf8_fuzzy(src: &str) -> Result<String, Error<'static>> {
with_tmp_pool(|pool| unsafe {
let src_cstr = std::ffi::CString::new(src)?;
let dest_ptr =
subversion_sys::svn_utf_cstring_from_utf8_fuzzy(src_cstr.as_ptr(), pool.as_mut_ptr());
if dest_ptr.is_null() {
return Err(Error::from_message("Fuzzy UTF-8 conversion returned null"));
}
let dest_cstr = std::ffi::CStr::from_ptr(dest_ptr);
Ok(dest_cstr.to_str()?.to_string())
})
}
pub fn cstring_utf8_width(cstr: &str) -> Option<isize> {
let cstr_c = std::ffi::CString::new(cstr).ok()?;
unsafe {
let width = subversion_sys::svn_utf_cstring_utf8_width(cstr_c.as_ptr());
if width == -1 {
None
} else {
Some(width as isize)
}
}
}
pub trait ToUtf8 {
fn to_utf8(&self) -> Result<String, Error<'static>>;
fn to_utf8_from(&self, frompage: &str) -> Result<String, Error<'static>>;
}
impl ToUtf8 for str {
fn to_utf8(&self) -> Result<String, Error<'static>> {
cstring_to_utf8(self)
}
fn to_utf8_from(&self, frompage: &str) -> Result<String, Error<'static>> {
cstring_to_utf8_ex(self, frompage)
}
}
impl ToUtf8 for String {
fn to_utf8(&self) -> Result<String, Error<'static>> {
cstring_to_utf8(self)
}
fn to_utf8_from(&self, frompage: &str) -> Result<String, Error<'static>> {
cstring_to_utf8_ex(self, frompage)
}
}
pub trait FromUtf8 {
fn from_utf8(&self) -> Result<String, Error<'static>>;
fn from_utf8_to(&self, topage: &str) -> Result<String, Error<'static>>;
fn from_utf8_fuzzy(&self) -> Result<String, Error<'static>>;
}
impl FromUtf8 for str {
fn from_utf8(&self) -> Result<String, Error<'static>> {
cstring_from_utf8(self)
}
fn from_utf8_to(&self, topage: &str) -> Result<String, Error<'static>> {
cstring_from_utf8_ex(self, topage)
}
fn from_utf8_fuzzy(&self) -> Result<String, Error<'static>> {
cstring_from_utf8_fuzzy(self)
}
}
impl FromUtf8 for String {
fn from_utf8(&self) -> Result<String, Error<'static>> {
cstring_from_utf8(self)
}
fn from_utf8_to(&self, topage: &str) -> Result<String, Error<'static>> {
cstring_from_utf8_ex(self, topage)
}
fn from_utf8_fuzzy(&self) -> Result<String, Error<'static>> {
cstring_from_utf8_fuzzy(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_initialize() {
initialize(true);
initialize(false);
}
#[test]
fn test_utf8_width() {
assert_eq!(cstring_utf8_width("hello"), Some(5));
assert_eq!(cstring_utf8_width(""), Some(0));
assert_eq!(cstring_utf8_width("a"), Some(1));
let width = cstring_utf8_width("café");
assert!(width.is_some());
assert!(width.unwrap() >= 4); }
#[test]
fn test_utf8_round_trip() {
initialize(true);
let original = "Hello, UTF-8 world! 🌍";
let utf8_string = cstring_to_utf8(original).unwrap();
assert_eq!(utf8_string, original);
let final_string = cstring_from_utf8(&utf8_string).unwrap();
assert_eq!(final_string, original);
}
#[test]
fn test_fuzzy_conversion() {
initialize(true);
let utf8_string = "Hello, world!";
let converted = cstring_from_utf8_fuzzy(utf8_string).unwrap();
assert_eq!(converted, "Hello, world!");
}
#[test]
fn test_traits() {
let test_str = "Hello, world!";
assert_eq!(test_str.to_utf8().unwrap(), "Hello, world!");
let test_string = test_str.to_string();
assert_eq!(test_string.to_utf8().unwrap(), "Hello, world!");
assert_eq!(test_str.from_utf8().unwrap(), "Hello, world!");
assert_eq!(test_str.from_utf8_fuzzy().unwrap(), "Hello, world!");
}
#[test]
fn test_explicit_encoding() {
let test_str = "Hello";
assert_eq!(test_str.to_utf8_from("UTF-8").unwrap(), "Hello");
assert_eq!(test_str.from_utf8_to("UTF-8").unwrap(), "Hello");
}
#[test]
fn test_empty_strings() {
let empty = "";
assert_eq!(empty.to_utf8().unwrap(), "");
assert_eq!(empty.from_utf8().unwrap(), "");
assert_eq!(empty.from_utf8_fuzzy().unwrap(), "");
assert_eq!(cstring_utf8_width(empty), Some(0));
}
#[test]
fn test_null_handling() {
let result = cstring_to_utf8("test\0ing");
assert!(result.is_err());
}
}