plugin_interfaces/
metadata.rs

1use serde::{Deserialize, Serialize};
2use std::os::raw::c_char;
3
4/// 插件元数据结构
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct PluginMetadata {
7    pub id: String,
8    pub disabled: bool,
9    pub name: String,
10    pub description: String,
11    pub version: String,
12    pub author: Option<String>,
13    pub library_path: Option<String>, // 动态库文件路径
14    pub config_path: String,          // 配置文件路径
15    pub instance_id: Option<String>,  // 插件实例ID,用于多实例支持
16}
17
18/// FFI安全的插件元数据结构
19/// 使用C风格的字符串指针
20#[repr(C)]
21#[derive(Copy, Clone)]
22pub struct PluginMetadataFFI {
23    pub id: *const c_char,
24    pub disabled: bool,
25    pub name: *const c_char,
26    pub description: *const c_char,
27    pub version: *const c_char,
28    pub author: *const c_char,       // 如果为null表示None
29    pub library_path: *const c_char, // 如果为null表示None
30    pub config_path: *const c_char,
31    pub instance_id: *const c_char, // 如果为null表示None
32}
33
34impl PluginMetadata {
35    /// 转换为FFI安全的结构
36    /// 注意:调用者需要负责释放返回的字符串内存
37    pub fn to_ffi(&self) -> PluginMetadataFFI {
38        use std::ffi::CString;
39
40        let id = CString::new(self.id.clone()).unwrap().into_raw();
41        let name = CString::new(self.name.clone()).unwrap().into_raw();
42        let description = CString::new(self.description.clone()).unwrap().into_raw();
43        let version = CString::new(self.version.clone()).unwrap().into_raw();
44        let config_path = CString::new(self.config_path.clone()).unwrap().into_raw();
45        let instance_id = if let Some(ref id) = self.instance_id {
46            CString::new(id.clone()).unwrap().into_raw()
47        } else {
48            std::ptr::null()
49        };
50
51        let author = if let Some(ref author) = self.author {
52            CString::new(author.clone()).unwrap().into_raw()
53        } else {
54            std::ptr::null()
55        };
56
57        let library_path = if let Some(ref path) = self.library_path {
58            CString::new(path.clone()).unwrap().into_raw()
59        } else {
60            std::ptr::null()
61        };
62
63        PluginMetadataFFI {
64            id,
65            disabled: self.disabled,
66            name,
67            description,
68            version,
69            author,
70            library_path,
71            config_path,
72            instance_id,
73        }
74    }
75}
76
77/// 释放FFI元数据结构中的字符串内存
78/// 必须在不再使用PluginMetadataFFI时调用
79pub unsafe fn free_plugin_metadata_ffi(metadata: PluginMetadataFFI) {
80    use std::ffi::CString;
81
82    if !metadata.id.is_null() {
83        let _ = CString::from_raw(metadata.id as *mut c_char);
84    }
85    if !metadata.name.is_null() {
86        let _ = CString::from_raw(metadata.name as *mut c_char);
87    }
88    if !metadata.description.is_null() {
89        let _ = CString::from_raw(metadata.description as *mut c_char);
90    }
91    if !metadata.version.is_null() {
92        let _ = CString::from_raw(metadata.version as *mut c_char);
93    }
94    if !metadata.config_path.is_null() {
95        let _ = CString::from_raw(metadata.config_path as *mut c_char);
96    }
97    if !metadata.author.is_null() {
98        let _ = CString::from_raw(metadata.author as *mut c_char);
99    }
100    if !metadata.library_path.is_null() {
101        let _ = CString::from_raw(metadata.library_path as *mut c_char);
102    }
103}
104
105/// 插件实例上下文
106/// 包含插件实例的所有状态信息
107#[derive(Debug, Clone)]
108pub struct PluginInstanceContext {
109    pub instance_id: String,
110    pub metadata: PluginMetadata,
111    pub callbacks: Option<crate::callbacks::HostCallbacks>,
112}
113
114impl PluginInstanceContext {
115    /// 创建新的插件实例上下文
116    pub fn new(instance_id: String, metadata: PluginMetadata) -> Self {
117        Self {
118            instance_id,
119            metadata,
120            callbacks: None,
121        }
122    }
123
124    /// 设置回调函数
125    pub fn set_callbacks(&mut self, callbacks: crate::callbacks::HostCallbacks) {
126        self.callbacks = Some(callbacks);
127    }
128
129    /// 获取实例ID
130    pub fn get_instance_id(&self) -> &str {
131        &self.instance_id
132    }
133
134    /// 获取元数据
135    pub fn get_metadata(&self) -> &PluginMetadata {
136        &self.metadata
137    }
138
139    /// 获取回调函数
140    pub fn get_callbacks(&self) -> Option<&crate::callbacks::HostCallbacks> {
141        self.callbacks.as_ref()
142    }
143
144    /// 向前端发送消息
145    pub fn send_to_frontend(&self, event: &str, payload: &str) -> bool {
146        if let Some(callbacks) = &self.callbacks {
147            use std::ffi::CString;
148            if let (Ok(event_str), Ok(payload_str)) = (CString::new(event), CString::new(payload)) {
149                return (callbacks.send_to_frontend)(event_str.as_ptr(), payload_str.as_ptr());
150            }
151        }
152        false
153    }
154
155    /// 获取应用配置
156    pub fn get_app_config(&self, key: &str) -> Option<String> {
157        if let Some(callbacks) = &self.callbacks {
158            use std::ffi::CString;
159            if let Ok(key_str) = CString::new(key) {
160                let result_ptr = (callbacks.get_app_config)(key_str.as_ptr());
161                if !result_ptr.is_null() {
162                    unsafe {
163                        let c_str = std::ffi::CStr::from_ptr(result_ptr);
164                        return c_str.to_str().ok().map(|s| s.to_string());
165                    }
166                }
167            }
168        }
169        None
170    }
171
172    /// 调用其他插件
173    pub fn call_other_plugin(&self, plugin_id: &str, message: &str) -> Option<String> {
174        if let Some(callbacks) = &self.callbacks {
175            use std::ffi::CString;
176            if let (Ok(id_str), Ok(msg_str)) = (CString::new(plugin_id), CString::new(message)) {
177                let result_ptr = (callbacks.call_other_plugin)(id_str.as_ptr(), msg_str.as_ptr());
178                if !result_ptr.is_null() {
179                    unsafe {
180                        let c_str = std::ffi::CStr::from_ptr(result_ptr);
181                        return c_str.to_str().ok().map(|s| s.to_string());
182                    }
183                }
184            }
185        }
186        None
187    }
188}
189
190/// 将 FFI 元数据转换为 Rust 元数据
191pub unsafe fn convert_ffi_to_metadata(metadata_ffi: PluginMetadataFFI) -> PluginMetadata {
192    use std::ffi::CStr;
193
194    let id = if !metadata_ffi.id.is_null() {
195        CStr::from_ptr(metadata_ffi.id)
196            .to_string_lossy()
197            .to_string()
198    } else {
199        String::new()
200    };
201
202    let name = if !metadata_ffi.name.is_null() {
203        CStr::from_ptr(metadata_ffi.name)
204            .to_string_lossy()
205            .to_string()
206    } else {
207        String::new()
208    };
209
210    let description = if !metadata_ffi.description.is_null() {
211        CStr::from_ptr(metadata_ffi.description)
212            .to_string_lossy()
213            .to_string()
214    } else {
215        String::new()
216    };
217
218    let version = if !metadata_ffi.version.is_null() {
219        CStr::from_ptr(metadata_ffi.version)
220            .to_string_lossy()
221            .to_string()
222    } else {
223        String::new()
224    };
225
226    let author = if !metadata_ffi.author.is_null() {
227        Some(
228            CStr::from_ptr(metadata_ffi.author)
229                .to_string_lossy()
230                .to_string(),
231        )
232    } else {
233        None
234    };
235
236    let library_path = if !metadata_ffi.library_path.is_null() {
237        Some(
238            CStr::from_ptr(metadata_ffi.library_path)
239                .to_string_lossy()
240                .to_string(),
241        )
242    } else {
243        None
244    };
245
246    let config_path = if !metadata_ffi.config_path.is_null() {
247        CStr::from_ptr(metadata_ffi.config_path)
248            .to_string_lossy()
249            .to_string()
250    } else {
251        String::new()
252    };
253
254    let instance_id = if !metadata_ffi.instance_id.is_null() {
255        Some(
256            CStr::from_ptr(metadata_ffi.instance_id)
257                .to_string_lossy()
258                .to_string(),
259        )
260    } else {
261        None
262    };
263
264    PluginMetadata {
265        id,
266        disabled: metadata_ffi.disabled,
267        name,
268        description,
269        version,
270        author,
271        library_path,
272        config_path,
273        instance_id,
274    }
275}