use std::ffi::CStr;
use std::marker::PhantomData;
use std::os::raw::c_char;
#[repr(transparent)]
pub struct FfiStr<'a> {
cstr: *const c_char,
_boo: PhantomData<&'a ()>,
}
impl<'a> FfiStr<'a> {
#[inline]
pub unsafe fn from_raw(ptr: *const c_char) -> Self {
Self {
cstr: ptr,
_boo: PhantomData,
}
}
#[inline]
pub fn from_cstr(cstr: &'a CStr) -> Self {
Self {
cstr: cstr.as_ptr(),
_boo: PhantomData,
}
}
#[inline]
pub fn as_str(&self) -> &'a str {
self.as_opt_str()
.expect("Unexpected null string pointer passed to rust")
}
pub fn as_opt_str(&self) -> Option<&'a str> {
if self.cstr.is_null() {
return None;
}
unsafe {
match std::ffi::CStr::from_ptr(self.cstr).to_str() {
Ok(s) => Some(s),
Err(e) => {
log::error!("Invalid UTF-8 was passed to rust! {:?}", e);
None
}
}
}
}
pub fn into_opt_string(self) -> Option<String> {
if !self.cstr.is_null() {
unsafe { Some(CStr::from_ptr(self.cstr).to_string_lossy().to_string()) }
} else {
None
}
}
#[inline]
pub fn into_string(self) -> String {
self.into_opt_string()
.expect("Unexpected null string pointer passed to rust")
}
}
impl<'a> std::fmt::Debug for FfiStr<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(s) = self.as_opt_str() {
write!(f, "FfiStr({:?})", s)
} else {
write!(f, "FfiStr(null)")
}
}
}
impl<'a> From<FfiStr<'a>> for String {
#[inline]
fn from(f: FfiStr<'a>) -> Self {
f.into_string()
}
}
impl<'a> From<FfiStr<'a>> for Option<String> {
#[inline]
fn from(f: FfiStr<'a>) -> Self {
f.into_opt_string()
}
}
impl<'a> From<FfiStr<'a>> for Option<&'a str> {
#[inline]
fn from(f: FfiStr<'a>) -> Self {
f.as_opt_str()
}
}
impl<'a> From<FfiStr<'a>> for &'a str {
#[inline]
fn from(f: FfiStr<'a>) -> Self {
f.as_str()
}
}
impl<'a> PartialEq for FfiStr<'a> {
#[inline]
fn eq(&self, other: &FfiStr<'a>) -> bool {
self.as_opt_str() == other.as_opt_str()
}
}
impl<'a> PartialEq<str> for FfiStr<'a> {
#[inline]
fn eq(&self, other: &str) -> bool {
self.as_opt_str() == Some(other)
}
}
impl<'a, 'b> PartialEq<&'b str> for FfiStr<'a> {
#[inline]
fn eq(&self, other: &&'b str) -> bool {
self.as_opt_str() == Some(*other)
}
}
impl<'a> PartialEq<FfiStr<'a>> for str {
#[inline]
fn eq(&self, other: &FfiStr<'a>) -> bool {
Some(self) == other.as_opt_str()
}
}
impl<'a, 'b> PartialEq<FfiStr<'a>> for &'b str {
#[inline]
fn eq(&self, other: &FfiStr<'a>) -> bool {
Some(*self) == other.as_opt_str()
}
}