use std::{
ffi::{CStr, CString, c_void},
fmt::Debug,
mem::MaybeUninit,
path::PathBuf,
};
use serde::{Serialize, de::DeserializeOwned};
use tracing::{debug, error};
use crate::{PluginApp, PluginHandler, PluginHost, sys};
pub mod config;
pub trait Config: Serialize + DeserializeOwned + Send + Debug + 'static {}
impl<T: Serialize + DeserializeOwned + Send + Debug + 'static> Config for T {}
impl<A: PluginApp> PluginHandler<A> {
pub fn load_settings(&self, data: *mut c_void) -> Option<A::Config> {
match self.get_host() {
Some(host) => {
let s = host.plugin_get_setting_string(data, "_", 0 as _);
if !s.is_null() {
let config = unsafe { CStr::from_ptr(s as _) };
debug!(
config = %config.to_str().unwrap_or("Invalid UTF-8"),
"Plugin config",
);
match serde_json::from_slice(config.to_bytes()) {
Ok(config) => Some(config),
Err(e) => {
error!(%e, "Plugin config parse error");
None
}
}
} else {
None
}
}
None if !data.is_null() => {
let config = *unsafe { Box::from_raw(data as *mut A::Config) };
debug!(?config, "Plugin config");
Some(config)
}
None => None,
}
}
pub fn save_settings(&self, data: *mut c_void) -> *mut c_void {
let config = unsafe { self.app() }.config();
let config = serde_json::to_string(config).unwrap();
debug!(%config, "Plugin save settings");
self.host().plugin_set_setting_string(data, "_", &config);
1 as _
}
}
impl PluginHost {
fn os_get_app_data_path_cat_filename_common(&self, name: &str, filename: &str) -> PathBuf {
let os_get_app_data_path_cat_filename: unsafe extern "system" fn(
filename: *const sys::everything_plugin_utf8_t,
cbuf: *mut sys::everything_plugin_utf8_buf_t,
) = unsafe { self.get(name).unwrap_unchecked() };
let filename = CString::new(filename).unwrap();
let mut cbuf = MaybeUninit::uninit();
self.utf8_buf_init(cbuf.as_mut_ptr());
unsafe { os_get_app_data_path_cat_filename(filename.as_ptr() as _, cbuf.as_mut_ptr()) };
self.utf8_buf_into_string(cbuf.as_mut_ptr()).into()
}
pub fn os_get_app_data_path(&self) -> PathBuf {
self.os_get_app_data_path_cat_filename("")
}
pub fn os_get_app_data_path_cat_filename(&self, filename: &str) -> PathBuf {
self.os_get_app_data_path_cat_filename_common("os_get_app_data_path_cat_filename", filename)
}
pub fn os_get_local_app_data_path(&self) -> PathBuf {
self.os_get_local_app_data_path_cat_filename("")
}
pub fn os_get_local_app_data_path_cat_filename(&self, filename: &str) -> PathBuf {
self.os_get_app_data_path_cat_filename_common(
"os_get_local_app_data_path_cat_filename",
filename,
)
}
pub fn plugin_get_setting_string(
&self,
data: *mut c_void,
name: &str,
current_string: *mut sys::everything_plugin_utf8_t,
) -> *mut sys::everything_plugin_utf8_t {
let plugin_get_setting_string: unsafe extern "system" fn(
sorted_list: *mut c_void,
name: *const sys::everything_plugin_utf8_t,
current_string: *mut sys::everything_plugin_utf8_t,
) -> *mut sys::everything_plugin_utf8_t =
unsafe { self.get("plugin_get_setting_string").unwrap_unchecked() };
let name = CString::new(name).unwrap();
unsafe { plugin_get_setting_string(data, name.as_ptr() as _, current_string) }
}
pub fn plugin_set_setting_string(&self, data: *mut c_void, name: &str, value: &str) {
debug_assert!(
!value.contains('\n'),
"setting string value must be single-line"
);
let plugin_set_setting_string: unsafe extern "system" fn(
output_stream: sys::everything_plugin_output_stream_t,
name: *const sys::everything_plugin_utf8_t,
value: *const sys::everything_plugin_utf8_t,
) = unsafe { self.get("plugin_set_setting_string").unwrap_unchecked() };
let name = CString::new(name).unwrap();
let value = CString::new(value).unwrap();
unsafe { plugin_set_setting_string(data, name.as_ptr() as _, value.as_ptr() as _) };
}
pub fn plugin_setting_json_path(&self) -> PathBuf {
self.os_get_app_data_path().join("plugins.json")
}
}