1use super::{Plugin, PluginError, PluginResult, PluginType};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::sync::Arc;
7
8pub const PLUGIN_API_VERSION: &str = "1.0.0";
10pub const MIN_SUPPORTED_VERSION: &str = "1.0.0";
11pub const MAX_SUPPORTED_VERSION: &str = "1.99.99";
12
13pub trait PluginApi: Send + Sync {
15 fn api_version(&self) -> &str;
17
18 fn initialize_api(&mut self, host_info: &HostInfo) -> PluginResult<()>;
20
21 fn get_plugin_info(&self) -> PluginInfo;
23
24 fn handle_api_call(&self, call: &ApiCall) -> PluginResult<ApiResponse>;
26
27 fn notify(&self, event: &PluginEvent) -> PluginResult<()>;
29
30 fn supports_feature(&self, feature: &str) -> bool;
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct HostInfo {
37 pub name: String,
38 pub version: String,
39 pub api_version: String,
40 pub capabilities: Vec<String>,
41 pub configuration: HashMap<String, serde_json::Value>,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct PluginInfo {
47 pub name: String,
48 pub version: String,
49 pub description: String,
50 pub author: String,
51 pub plugin_type: PluginType,
52 pub api_version: String,
53 pub supported_features: Vec<String>,
54 pub required_permissions: Vec<super::Permission>,
55 pub metadata: HashMap<String, serde_json::Value>,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct ApiCall {
61 pub id: String,
62 pub method: String,
63 pub params: serde_json::Value,
64 pub context: Option<CallContext>,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct CallContext {
70 pub user_id: Option<String>,
71 pub session_id: Option<String>,
72 pub trace_id: Option<String>,
73 pub metadata: HashMap<String, String>,
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct ApiResponse {
79 pub id: String,
80 pub success: bool,
81 pub result: Option<serde_json::Value>,
82 pub error: Option<String>,
83 pub metadata: HashMap<String, serde_json::Value>,
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
88pub enum PluginEvent {
89 Loaded,
91 Unloading,
93 HostShutdown,
95 ConfigChanged(serde_json::Value),
97 ResourceStateChanged {
99 cpu_usage: f32,
100 memory_usage: f32,
101 available_memory: u64,
102 },
103 AudioStateChanged {
105 sample_rate: u32,
106 buffer_size: u32,
107 channels: u32,
108 },
109 Custom {
111 event_type: String,
112 data: serde_json::Value,
113 },
114}
115
116pub struct ApiRegistry {
118 apis: HashMap<String, Arc<dyn PluginApi>>,
119 host_info: HostInfo,
120}
121
122impl ApiRegistry {
123 pub fn new() -> Self {
125 Self {
126 apis: HashMap::new(),
127 host_info: HostInfo {
128 name: "VoiRS CLI".to_string(),
129 version: env!("CARGO_PKG_VERSION").to_string(),
130 api_version: PLUGIN_API_VERSION.to_string(),
131 capabilities: vec![
132 "audio_synthesis".to_string(),
133 "voice_management".to_string(),
134 "batch_processing".to_string(),
135 "real_time_synthesis".to_string(),
136 "plugin_system".to_string(),
137 ],
138 configuration: HashMap::new(),
139 },
140 }
141 }
142
143 pub fn register_api(&mut self, name: String, api: Arc<dyn PluginApi>) -> PluginResult<()> {
145 if self.apis.contains_key(&name) {
146 return Err(PluginError::LoadingFailed(format!(
147 "API {} already registered",
148 name
149 )));
150 }
151
152 if !self.is_version_compatible(api.api_version()) {
154 return Err(PluginError::ApiVersionMismatch {
155 expected: PLUGIN_API_VERSION.to_string(),
156 actual: api.api_version().to_string(),
157 });
158 }
159
160 self.apis.insert(name, api);
161 Ok(())
162 }
163
164 pub fn unregister_api(&mut self, name: &str) -> PluginResult<()> {
166 self.apis
167 .remove(name)
168 .ok_or_else(|| PluginError::NotFound(name.to_string()))?;
169 Ok(())
170 }
171
172 pub fn get_api(&self, name: &str) -> Option<Arc<dyn PluginApi>> {
174 self.apis.get(name).cloned()
175 }
176
177 pub fn list_apis(&self) -> Vec<String> {
179 self.apis.keys().cloned().collect()
180 }
181
182 pub fn initialize_all(&self) -> PluginResult<()> {
184 for (name, api) in &self.apis {
185 let mut api_mut = api.clone();
186 tracing::info!("Initialized API: {}", name);
189 }
190 Ok(())
191 }
192
193 pub fn broadcast_event(&self, event: &PluginEvent) -> PluginResult<()> {
195 for (name, api) in &self.apis {
196 if let Err(e) = api.notify(event) {
197 tracing::error!("Failed to notify API {}: {}", name, e);
198 }
199 }
200 Ok(())
201 }
202
203 pub fn call_api(&self, api_name: &str, call: &ApiCall) -> PluginResult<ApiResponse> {
205 let api = self
206 .apis
207 .get(api_name)
208 .ok_or_else(|| PluginError::NotFound(api_name.to_string()))?;
209
210 api.handle_api_call(call)
211 }
212
213 fn is_version_compatible(&self, version: &str) -> bool {
215 let min_parts: Vec<u32> = MIN_SUPPORTED_VERSION
217 .split('.')
218 .filter_map(|s| s.parse().ok())
219 .collect();
220 let max_parts: Vec<u32> = MAX_SUPPORTED_VERSION
221 .split('.')
222 .filter_map(|s| s.parse().ok())
223 .collect();
224 let version_parts: Vec<u32> = version.split('.').filter_map(|s| s.parse().ok()).collect();
225
226 if version_parts.len() != 3 || min_parts.len() != 3 || max_parts.len() != 3 {
227 return false;
228 }
229
230 version_parts >= min_parts && version_parts <= max_parts
232 }
233
234 pub fn get_host_info(&self) -> &HostInfo {
236 &self.host_info
237 }
238
239 pub fn update_host_config(&mut self, key: String, value: serde_json::Value) {
241 self.host_info.configuration.insert(key, value);
242 }
243}
244
245impl Default for ApiRegistry {
246 fn default() -> Self {
247 Self::new()
248 }
249}
250
251pub mod utils {
253 use super::*;
254
255 pub fn create_api_call(method: &str, params: serde_json::Value) -> ApiCall {
257 ApiCall {
258 id: generate_call_id(),
259 method: method.to_string(),
260 params,
261 context: None,
262 }
263 }
264
265 pub fn create_api_call_with_context(
267 method: &str,
268 params: serde_json::Value,
269 context: CallContext,
270 ) -> ApiCall {
271 ApiCall {
272 id: generate_call_id(),
273 method: method.to_string(),
274 params,
275 context: Some(context),
276 }
277 }
278
279 pub fn create_success_response(call_id: &str, result: serde_json::Value) -> ApiResponse {
281 ApiResponse {
282 id: call_id.to_string(),
283 success: true,
284 result: Some(result),
285 error: None,
286 metadata: HashMap::new(),
287 }
288 }
289
290 pub fn create_error_response(call_id: &str, error: &str) -> ApiResponse {
292 ApiResponse {
293 id: call_id.to_string(),
294 success: false,
295 result: None,
296 error: Some(error.to_string()),
297 metadata: HashMap::new(),
298 }
299 }
300
301 fn generate_call_id() -> String {
303 use std::time::{SystemTime, UNIX_EPOCH};
304 let timestamp = SystemTime::now()
305 .duration_since(UNIX_EPOCH)
306 .unwrap()
307 .as_nanos();
308 format!("call_{}", timestamp)
309 }
310
311 pub fn is_valid_method_name(method: &str) -> bool {
313 !method.is_empty()
314 && method
315 .chars()
316 .all(|c| c.is_alphanumeric() || c == '_' || c == '.')
317 }
318
319 pub fn extract_plugin_type(call: &ApiCall) -> Option<PluginType> {
321 call.context
322 .as_ref()
323 .and_then(|ctx| ctx.metadata.get("plugin_type"))
324 .and_then(|t| match t.as_str() {
325 "Effect" => Some(PluginType::Effect),
326 "Voice" => Some(PluginType::Voice),
327 "Processor" => Some(PluginType::Processor),
328 "Extension" => Some(PluginType::Extension),
329 _ => None,
330 })
331 }
332}
333
334#[cfg(test)]
335mod tests {
336 use super::*;
337
338 #[test]
339 fn test_api_registry_creation() {
340 let registry = ApiRegistry::new();
341 assert_eq!(registry.list_apis().len(), 0);
342 assert_eq!(registry.get_host_info().name, "VoiRS CLI");
343 assert_eq!(registry.get_host_info().api_version, PLUGIN_API_VERSION);
344 }
345
346 #[test]
347 fn test_version_compatibility() {
348 let registry = ApiRegistry::new();
349 assert!(registry.is_version_compatible("1.0.0"));
350 assert!(registry.is_version_compatible("1.0.1"));
351 assert!(registry.is_version_compatible("1.1.0"));
352 assert!(!registry.is_version_compatible("2.0.0"));
353 assert!(!registry.is_version_compatible("0.9.0"));
354 }
355
356 #[test]
357 fn test_api_call_creation() {
358 let call = utils::create_api_call("test_method", serde_json::json!({"param": "value"}));
359 assert_eq!(call.method, "test_method");
360 assert_eq!(call.params["param"], "value");
361 assert!(call.id.starts_with("call_"));
362 }
363
364 #[test]
365 fn test_api_response_creation() {
366 let response =
367 utils::create_success_response("test_id", serde_json::json!({"result": "ok"}));
368 assert_eq!(response.id, "test_id");
369 assert!(response.success);
370 assert_eq!(response.result.unwrap()["result"], "ok");
371
372 let error_response = utils::create_error_response("test_id", "test error");
373 assert_eq!(error_response.id, "test_id");
374 assert!(!error_response.success);
375 assert_eq!(error_response.error.unwrap(), "test error");
376 }
377
378 #[test]
379 fn test_method_name_validation() {
380 assert!(utils::is_valid_method_name("valid_method"));
381 assert!(utils::is_valid_method_name("method.with.dots"));
382 assert!(utils::is_valid_method_name("method123"));
383 assert!(!utils::is_valid_method_name(""));
384 assert!(!utils::is_valid_method_name("invalid-method"));
385 assert!(!utils::is_valid_method_name("invalid method"));
386 }
387
388 #[test]
389 fn test_host_info_structure() {
390 let registry = ApiRegistry::new();
391 let host_info = registry.get_host_info();
392
393 assert!(!host_info.name.is_empty());
394 assert!(!host_info.version.is_empty());
395 assert!(!host_info.api_version.is_empty());
396 assert!(!host_info.capabilities.is_empty());
397 assert!(host_info
398 .capabilities
399 .contains(&"audio_synthesis".to_string()));
400 }
401
402 #[test]
403 fn test_plugin_event_serialization() {
404 let event = PluginEvent::ConfigChanged(serde_json::json!({"setting": "value"}));
405 let serialized = serde_json::to_string(&event).unwrap();
406 let deserialized: PluginEvent = serde_json::from_str(&serialized).unwrap();
407
408 match deserialized {
409 PluginEvent::ConfigChanged(config) => {
410 assert_eq!(config["setting"], "value");
411 }
412 _ => panic!("Unexpected event type"),
413 }
414 }
415}