1use core::{ffi::c_void, marker::PhantomData};
6#[path = "../vendor/borrowed_abi.rs"]
7mod abi;
8pub use abi::{CodeBorrowedApi, CodeValueInfo};
9
10#[derive(Clone, Copy)]
12pub struct BorrowedValue<'a> {
13 api: &'a CodeBorrowedApi,
14 value: *const c_void,
15 _borrow: PhantomData<&'a c_void>,
16}
17
18impl<'a> BorrowedValue<'a> {
19 pub unsafe fn from_raw(api: *const CodeBorrowedApi, value: *const c_void) -> Option<Self> {
25 if api.is_null() || value.is_null() {
26 return None;
27 }
28 if api.cast::<u32>().read() != 1 {
30 return None;
31 }
32 let api = &*api;
33 Some(Self {
34 api,
35 value,
36 _borrow: PhantomData,
37 })
38 }
39
40 pub fn info(self) -> CodeValueInfo {
41 unsafe { (self.api.inspect)(self.value) }
43 }
44
45 pub fn field(self, key: &str) -> Option<Self> {
46 let value = unsafe { (self.api.field)(self.value, key.as_ptr(), key.len() as u64) };
48 if value.is_null() {
49 None
50 } else {
51 Some(Self { value, ..self })
52 }
53 }
54
55 pub fn text(self) -> Option<&'a str> {
56 let info = self.info();
57 if info.tag != 1 {
58 return None;
59 }
60 if info.length == 0 {
61 return Some("");
62 }
63 let bytes = unsafe { core::slice::from_raw_parts(info.bytes, info.length as usize) };
65 Some(unsafe { core::str::from_utf8_unchecked(bytes) })
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 unsafe extern "C" fn inspect(_: *const c_void) -> CodeValueInfo {
75 CodeValueInfo {
76 tag: 3,
77 length: 0,
78 bytes: core::ptr::null(),
79 }
80 }
81 unsafe extern "C" fn field(_: *const c_void, _: *const u8, _: u64) -> *const c_void {
82 core::ptr::null()
83 }
84 #[test]
85 fn unsupported_versions_and_missing_views_decline_before_reading_values() {
86 let version = 9_u32;
87 unsafe {
89 assert!(BorrowedValue::from_raw(
90 (&version as *const u32).cast(),
91 core::ptr::NonNull::<u8>::dangling().as_ptr().cast()
92 )
93 .is_none());
94 assert!(BorrowedValue::from_raw(core::ptr::null(), core::ptr::null()).is_none());
95 let api = CodeBorrowedApi {
96 version: 1,
97 inspect,
98 field,
99 };
100 let opaque = 0_u8;
101 let view = BorrowedValue::from_raw(&api, (&opaque as *const u8).cast()).unwrap();
102 assert_eq!(view.info().tag, 3);
103 assert!(view.field("").is_none());
104 assert!(view.text().is_none());
105 }
106 }
107}