Compare commits

...

1 Commits

Author SHA1 Message Date
zach
d704a5068e refactor!: make UserData more generic 2024-03-18 10:24:47 -07:00
4 changed files with 19 additions and 17 deletions

View File

@@ -1,7 +1,7 @@
use extism::*;
// pretend this is redis or something :)
type KVStore = std::collections::BTreeMap<String, Vec<u8>>;
type KVStore = std::sync::Arc<std::sync::Mutex<std::collections::BTreeMap<String, Vec<u8>>>>;
// When a first argument separated with a semicolon is provided to `host_fn` it is used as the
// variable name and type for the `UserData` parameter

View File

@@ -81,18 +81,18 @@ pub(crate) enum UserDataHandle {
/// using `UserData::get`. The `C` data is stored as a pointer and cleanup function and isn't usable from Rust. The cleanup function
/// will be called when the inner `CPtr` is dropped.
#[derive(Debug)]
pub enum UserData<T: Sized> {
pub enum UserData<T: Sync + Clone + Sized> {
C(Arc<CPtr>),
Rust(Arc<std::sync::Mutex<T>>),
Rust(T),
}
impl<T: Default> Default for UserData<T> {
impl<T: Default + Sync + Clone> Default for UserData<T> {
fn default() -> Self {
UserData::new(T::default())
}
}
impl<T> Clone for UserData<T> {
impl<T: Sync + Clone> Clone for UserData<T> {
fn clone(&self) -> Self {
match self {
UserData::C(ptr) => UserData::C(ptr.clone()),
@@ -101,7 +101,7 @@ impl<T> Clone for UserData<T> {
}
}
impl<T> UserData<T> {
impl<T: Sync + Clone> UserData<T> {
/// Create a new `UserData` from an existing pointer and free function, this is used
/// by the C API to wrap C pointers into user data
pub(crate) fn new_pointer(
@@ -126,12 +126,11 @@ impl<T> UserData<T> {
///
/// This will wrap the provided value in a reference-counted mutex
pub fn new(x: T) -> Self {
let data = Arc::new(std::sync::Mutex::new(x));
UserData::Rust(data)
UserData::Rust(x)
}
/// Get a copy of the inner value
pub fn get(&self) -> Result<Arc<std::sync::Mutex<T>>, Error> {
pub fn get(&self) -> Result<T, Error> {
match self {
UserData::C { .. } => anyhow::bail!("C UserData should not be used from Rust"),
UserData::Rust(data) => Ok(data.clone()),
@@ -150,8 +149,8 @@ impl Drop for CPtr {
}
}
unsafe impl<T> Send for UserData<T> {}
unsafe impl<T> Sync for UserData<T> {}
unsafe impl<T: Sync + Clone> Send for UserData<T> {}
unsafe impl<T: Sync + Clone> Sync for UserData<T> {}
unsafe impl Send for CPtr {}
unsafe impl Sync for CPtr {}
@@ -180,7 +179,7 @@ pub struct Function {
impl Function {
/// Create a new host function
pub fn new<T: 'static, F>(
pub fn new<T: 'static + Sync + Clone, F>(
name: impl Into<String>,
args: impl IntoIterator<Item = ValType>,
returns: impl IntoIterator<Item = ValType>,
@@ -211,7 +210,9 @@ impl Function {
namespace: None,
_user_data: match &user_data {
UserData::C(ptr) => UserDataHandle::C(ptr.clone()),
UserData::Rust(x) => UserDataHandle::Rust(x.clone()),
UserData::Rust(x) => {
UserDataHandle::Rust(std::sync::Arc::new(std::sync::Mutex::new(x.clone())))
}
},
}
}

View File

@@ -60,7 +60,7 @@ impl<'a> PluginBuilder<'a> {
}
/// Add a single host function
pub fn with_function<T: 'static, F>(
pub fn with_function<T: Sync + Clone + 'static, F>(
mut self,
name: impl Into<String>,
args: impl IntoIterator<Item = ValType>,
@@ -80,7 +80,7 @@ impl<'a> PluginBuilder<'a> {
}
/// Add a single host function in a specific namespace
pub fn with_function_in_namespace<T: 'static, F>(
pub fn with_function_in_namespace<T: Sync + Clone + 'static, F>(
mut self,
namespace: impl Into<String>,
name: impl Into<String>,

View File

@@ -453,7 +453,7 @@ fn hello_world_user_data(
_plugin: &mut CurrentPlugin,
inputs: &[Val],
outputs: &mut [Val],
user_data: UserData<std::fs::File>,
user_data: UserData<std::sync::Arc<std::sync::Mutex<std::fs::File>>>,
) -> Result<(), Error> {
let data = user_data.get()?;
let mut data = data.lock().unwrap();
@@ -470,7 +470,8 @@ fn test_userdata() {
if path.exists() {
std::fs::remove_file(&path).unwrap();
}
let file = std::fs::File::create(&path).unwrap();
let file =
std::sync::Arc::new(std::sync::Mutex::new(std::fs::File::create(&path).unwrap()));
let f = Function::new(
"hello_world",
[PTR],