use core::{ffi::c_void, marker::PhantomData};
#[path = "../vendor/borrowed_abi.rs"]
mod abi;
pub use abi::{CodeBorrowedApi, CodeValueInfo};
#[derive(Clone, Copy)]
pub struct BorrowedValue<'a> {
api: &'a CodeBorrowedApi,
value: *const c_void,
_borrow: PhantomData<&'a c_void>,
}
impl<'a> BorrowedValue<'a> {
pub unsafe fn from_raw(api: *const CodeBorrowedApi, value: *const c_void) -> Option<Self> {
if api.is_null() || value.is_null() {
return None;
}
if api.cast::<u32>().read() != 1 {
return None;
}
let api = &*api;
Some(Self {
api,
value,
_borrow: PhantomData,
})
}
pub fn info(self) -> CodeValueInfo {
unsafe { (self.api.inspect)(self.value) }
}
pub fn field(self, key: &str) -> Option<Self> {
let value = unsafe { (self.api.field)(self.value, key.as_ptr(), key.len() as u64) };
if value.is_null() {
None
} else {
Some(Self { value, ..self })
}
}
pub fn text(self) -> Option<&'a str> {
let info = self.info();
if info.tag != 1 {
return None;
}
if info.length == 0 {
return Some("");
}
let bytes = unsafe { core::slice::from_raw_parts(info.bytes, info.length as usize) };
Some(unsafe { core::str::from_utf8_unchecked(bytes) })
}
}
#[cfg(test)]
mod tests {
use super::*;
unsafe extern "C" fn inspect(_: *const c_void) -> CodeValueInfo {
CodeValueInfo {
tag: 3,
length: 0,
bytes: core::ptr::null(),
}
}
unsafe extern "C" fn field(_: *const c_void, _: *const u8, _: u64) -> *const c_void {
core::ptr::null()
}
#[test]
fn unsupported_versions_and_missing_views_decline_before_reading_values() {
let version = 9_u32;
unsafe {
assert!(BorrowedValue::from_raw(
(&version as *const u32).cast(),
core::ptr::NonNull::<u8>::dangling().as_ptr().cast()
)
.is_none());
assert!(BorrowedValue::from_raw(core::ptr::null(), core::ptr::null()).is_none());
let api = CodeBorrowedApi {
version: 1,
inspect,
field,
};
let opaque = 0_u8;
let view = BorrowedValue::from_raw(&api, (&opaque as *const u8).cast()).unwrap();
assert_eq!(view.info().tag, 3);
assert!(view.field("").is_none());
assert!(view.text().is_none());
}
}
}