1use std::collections::HashMap;
7use std::sync::Arc;
8
9use parking_lot::RwLock;
10
11#[derive(Debug, Clone)]
13pub struct PluginMetadata {
14 pub name: String,
16 pub version: String,
18 pub description: String,
20 pub author: String,
22}
23
24impl PluginMetadata {
25 pub fn new(
27 name: impl Into<String>,
28 version: impl Into<String>,
29 description: impl Into<String>,
30 ) -> Self {
31 Self {
32 name: name.into(),
33 version: version.into(),
34 description: description.into(),
35 author: String::new(),
36 }
37 }
38
39 pub fn with_author(mut self, author: impl Into<String>) -> Self {
41 self.author = author.into();
42 self
43 }
44}
45
46pub trait AiExtension: Send + Sync {
48 fn name(&self) -> &str;
50
51 fn execute(&self, input: &str) -> Result<String, PluginError>;
53}
54
55pub trait DialectExtension: Send + Sync {
57 fn dialect_name(&self) -> &str;
59
60 fn translate(&self, sql: &str) -> Result<String, PluginError>;
62}
63
64pub trait MiddlewareExtension: Send + Sync {
66 fn name(&self) -> &str;
68
69 fn before_query(&self, sql: &str) -> Result<String, PluginError>;
71
72 fn after_query(&self, sql: &str, result: &str) -> Result<String, PluginError>;
74}
75
76#[derive(Debug, Clone)]
78pub enum PluginError {
79 NotFound(String),
81 ExecutionFailed(String),
83 RegistrationFailed(String),
85 ChainTooLong(String),
87}
88
89impl std::fmt::Display for PluginError {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 match self {
92 PluginError::NotFound(msg) => write!(f, "Plugin not found: {}", msg),
93 PluginError::ExecutionFailed(msg) => write!(f, "Execution failed: {}", msg),
94 PluginError::RegistrationFailed(msg) => write!(f, "Registration failed: {}", msg),
95 PluginError::ChainTooLong(msg) => write!(f, "Chain too long: {}", msg),
96 }
97 }
98}
99
100impl std::error::Error for PluginError {}
101
102pub trait SzOrmPlugin: Send + Sync {
106 fn metadata(&self) -> &PluginMetadata;
108
109 fn init(&self) -> Result<(), PluginError> {
111 Ok(())
112 }
113
114 fn ai_extension(&self) -> Option<&dyn AiExtension> {
116 None
117 }
118
119 fn dialect_extension(&self) -> Option<&dyn DialectExtension> {
121 None
122 }
123
124 fn middleware_extension(&self) -> Option<&dyn MiddlewareExtension> {
126 None
127 }
128}
129
130pub struct PluginRegistry {
134 plugins: RwLock<HashMap<String, Arc<dyn SzOrmPlugin>>>,
135}
136
137impl Default for PluginRegistry {
138 fn default() -> Self {
139 Self::new()
140 }
141}
142
143impl PluginRegistry {
144 pub fn new() -> Self {
146 Self {
147 plugins: RwLock::new(HashMap::new()),
148 }
149 }
150
151 pub fn register(&self, plugin: Arc<dyn SzOrmPlugin>) -> Result<(), PluginError> {
153 let metadata = plugin.metadata();
154 let name = metadata.name.clone();
155
156 plugin.init()?;
157
158 let mut plugins = self.plugins.write();
159 if plugins.contains_key(&name) {
160 return Err(PluginError::RegistrationFailed(format!(
161 "插件 {} 已存在",
162 name
163 )));
164 }
165 plugins.insert(name, plugin);
166 Ok(())
167 }
168
169 pub fn unregister(&self, name: &str) -> Result<(), PluginError> {
171 let mut plugins = self.plugins.write();
172 plugins
173 .remove(name)
174 .ok_or_else(|| PluginError::NotFound(name.to_string()))?;
175 Ok(())
176 }
177
178 pub fn get(&self, name: &str) -> Option<Arc<dyn SzOrmPlugin>> {
180 self.plugins.read().get(name).cloned()
181 }
182
183 pub fn list(&self) -> Vec<String> {
185 self.plugins.read().keys().cloned().collect()
186 }
187
188 pub fn len(&self) -> usize {
190 self.plugins.read().len()
191 }
192
193 pub fn is_empty(&self) -> bool {
195 self.plugins.read().is_empty()
196 }
197
198 pub fn execute_ai(&self, plugin_name: &str, input: &str) -> Result<String, PluginError> {
200 let plugin = self
201 .get(plugin_name)
202 .ok_or_else(|| PluginError::NotFound(plugin_name.to_string()))?;
203 let ext = plugin
204 .ai_extension()
205 .ok_or_else(|| PluginError::ExecutionFailed("插件无 AI 扩展".to_string()))?;
206 ext.execute(input)
207 }
208
209 pub fn translate_dialect(&self, plugin_name: &str, sql: &str) -> Result<String, PluginError> {
211 let plugin = self
212 .get(plugin_name)
213 .ok_or_else(|| PluginError::NotFound(plugin_name.to_string()))?;
214 let ext = plugin
215 .dialect_extension()
216 .ok_or_else(|| PluginError::ExecutionFailed("插件无方言扩展".to_string()))?;
217 ext.translate(sql)
218 }
219
220 pub fn before_query(&self, plugin_name: &str, sql: &str) -> Result<String, PluginError> {
222 let plugin = self
223 .get(plugin_name)
224 .ok_or_else(|| PluginError::NotFound(plugin_name.to_string()))?;
225 let ext = plugin
226 .middleware_extension()
227 .ok_or_else(|| PluginError::ExecutionFailed("插件无中间件扩展".to_string()))?;
228 ext.before_query(sql)
229 }
230
231 pub fn after_query(
233 &self,
234 plugin_name: &str,
235 sql: &str,
236 result: &str,
237 ) -> Result<String, PluginError> {
238 let plugin = self
239 .get(plugin_name)
240 .ok_or_else(|| PluginError::NotFound(plugin_name.to_string()))?;
241 let ext = plugin
242 .middleware_extension()
243 .ok_or_else(|| PluginError::ExecutionFailed("插件无中间件扩展".to_string()))?;
244 ext.after_query(sql, result)
245 }
246}
247#[cfg(feature = "composable-plugin")]
252mod composable {
253 use std::collections::HashMap;
254 use std::panic::{catch_unwind, AssertUnwindSafe};
255 use std::sync::Arc;
256 use std::time::{Duration, Instant};
257
258 use parking_lot::RwLock;
259
260 use super::{MiddlewareExtension, PluginError, SzOrmPlugin};
261
262 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
264 pub enum PluginState {
265 Enabled,
267 Disabled,
269 AutoDisabled,
271 }
272
273 pub struct PanicSafeRegistry {
278 plugins: RwLock<HashMap<String, (Arc<dyn SzOrmPlugin>, PluginState)>>,
279 }
280
281 impl Default for PanicSafeRegistry {
282 fn default() -> Self {
283 Self::new()
284 }
285 }
286
287 impl PanicSafeRegistry {
288 pub fn new() -> Self {
290 Self {
291 plugins: RwLock::new(HashMap::new()),
292 }
293 }
294
295 pub fn register_safe(&self, plugin: Arc<dyn SzOrmPlugin>) -> Result<(), PluginError> {
297 let metadata = plugin.metadata();
298 if metadata.name.is_empty() {
299 return Err(PluginError::RegistrationFailed(
300 "插件名称不能为空".to_string(),
301 ));
302 }
303 plugin.init()?;
304 let name = metadata.name.clone();
305 let mut plugins = self.plugins.write();
306 if plugins.contains_key(&name) {
307 return Err(PluginError::RegistrationFailed(format!(
308 "插件 {} 已存在",
309 name
310 )));
311 }
312 plugins.insert(name, (plugin, PluginState::Enabled));
313 Ok(())
314 }
315
316 pub fn invoke_safe<F, R>(&self, plugin_name: &str, f: F) -> Result<R, PluginError>
321 where
322 F: FnOnce(&dyn SzOrmPlugin) -> Result<R, PluginError>,
323 {
324 let (plugin, state) = {
325 let plugins = self.plugins.read();
326 plugins
327 .get(plugin_name)
328 .map(|(p, s)| (p.clone(), *s))
329 .ok_or_else(|| PluginError::NotFound(plugin_name.to_string()))?
330 };
331 if state != PluginState::Enabled {
332 return Err(PluginError::ExecutionFailed(format!(
333 "插件 {} 未启用(当前状态: {:?})",
334 plugin_name, state
335 )));
336 }
337 let result = catch_unwind(AssertUnwindSafe(|| f(plugin.as_ref())));
338 match result {
339 Ok(r) => r,
340 Err(panic_payload) => {
341 let msg = if let Some(s) = panic_payload.downcast_ref::<&str>() {
342 s.to_string()
343 } else if let Some(s) = panic_payload.downcast_ref::<String>() {
344 s.clone()
345 } else {
346 "未知 panic".to_string()
347 };
348 tracing::warn!(
349 plugin = plugin_name,
350 panic_msg = %msg,
351 "插件 panic,自动禁用"
352 );
353 let mut plugins = self.plugins.write();
354 if let Some(entry) = plugins.get_mut(plugin_name) {
355 entry.1 = PluginState::AutoDisabled;
356 }
357 Err(PluginError::ExecutionFailed(format!(
358 "插件 {} panic: {}",
359 plugin_name, msg
360 )))
361 }
362 }
363 }
364
365 pub fn state(&self, name: &str) -> Option<PluginState> {
367 self.plugins.read().get(name).map(|(_, s)| *s)
368 }
369
370 pub fn enable(&self, name: &str) -> Result<(), PluginError> {
372 let mut plugins = self.plugins.write();
373 let entry = plugins
374 .get_mut(name)
375 .ok_or_else(|| PluginError::NotFound(name.to_string()))?;
376 entry.1 = PluginState::Enabled;
377 Ok(())
378 }
379
380 pub fn list(&self) -> Vec<String> {
382 self.plugins.read().keys().cloned().collect()
383 }
384 }
385
386 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
388 pub enum SignatureStatus {
389 Signed,
391 Unsigned,
393 Invalid,
395 }
396
397 pub struct PluginSigner {
403 allow_unsigned: bool,
404 }
405
406 impl PluginSigner {
407 pub fn new(allow_unsigned: bool) -> Self {
411 Self { allow_unsigned }
412 }
413
414 pub fn verify(
424 &self,
425 plugin: &[u8],
426 signature: &[u8],
427 public_key: &[u8],
428 ) -> SignatureStatus {
429 if signature.is_empty() {
430 return SignatureStatus::Unsigned;
431 }
432 let expected = sz_orm_crypto::hmac_sha256(public_key, plugin);
433 if expected.as_slice() == signature {
434 SignatureStatus::Signed
435 } else {
436 SignatureStatus::Invalid
437 }
438 }
439
440 pub fn is_allowed(&self, status: SignatureStatus) -> bool {
442 match status {
443 SignatureStatus::Signed => true,
444 SignatureStatus::Unsigned => self.allow_unsigned,
445 SignatureStatus::Invalid => false,
446 }
447 }
448
449 pub fn sign(&self, plugin: &[u8], secret_key: &[u8]) -> Vec<u8> {
451 sz_orm_crypto::hmac_sha256(secret_key, plugin).to_vec()
452 }
453 }
454
455 const MAX_CHAIN_LEN: usize = 10;
457
458 pub struct MiddlewareChain {
463 chain: Vec<(i32, Arc<dyn MiddlewareExtension>)>,
464 last_latency: RwLock<Option<Duration>>,
465 }
466
467 impl Default for MiddlewareChain {
468 fn default() -> Self {
469 Self::new()
470 }
471 }
472
473 impl MiddlewareChain {
474 pub fn new() -> Self {
476 Self {
477 chain: Vec::new(),
478 last_latency: RwLock::new(None),
479 }
480 }
481
482 pub fn add(
484 &mut self,
485 order: i32,
486 middleware: Arc<dyn MiddlewareExtension>,
487 ) -> Result<(), PluginError> {
488 if self.chain.len() >= MAX_CHAIN_LEN {
489 return Err(PluginError::ChainTooLong(format!(
490 "中间件链长度超过上限 {}",
491 MAX_CHAIN_LEN
492 )));
493 }
494 let pos = self.chain.partition_point(|(o, _)| *o < order);
495 self.chain.insert(pos, (order, middleware));
496 Ok(())
497 }
498
499 pub fn before_query(&self, sql: &str) -> Result<String, PluginError> {
501 let start = Instant::now();
502 let mut current = sql.to_string();
503 for (_, mw) in &self.chain {
504 current = mw.before_query(¤t)?;
505 }
506 let latency = start.elapsed();
507 *self.last_latency.write() = Some(latency);
508 Ok(current)
509 }
510
511 pub fn after_query(&self, sql: &str, result: &str) -> Result<String, PluginError> {
513 let start = Instant::now();
514 let mut current = result.to_string();
515 for (_, mw) in self.chain.iter().rev() {
516 current = mw.after_query(sql, ¤t)?;
517 }
518 let latency = start.elapsed();
519 *self.last_latency.write() = Some(latency);
520 Ok(current)
521 }
522
523 pub fn last_chain_latency(&self) -> Duration {
525 self.last_latency.read().unwrap_or_default()
526 }
527
528 pub fn len(&self) -> usize {
530 self.chain.len()
531 }
532
533 pub fn is_empty(&self) -> bool {
535 self.chain.is_empty()
536 }
537 }
538
539 #[cfg(test)]
540 mod tests {
541 use super::super::PluginMetadata;
542 use super::*;
543
544 struct GoodPlugin;
545 impl SzOrmPlugin for GoodPlugin {
546 fn metadata(&self) -> &PluginMetadata {
547 use std::sync::OnceLock;
548 static META: OnceLock<PluginMetadata> = OnceLock::new();
549 META.get_or_init(|| PluginMetadata::new("good", "1.0.0", "test plugin"))
550 }
551 }
552
553 struct PanicPlugin;
554 impl SzOrmPlugin for PanicPlugin {
555 fn metadata(&self) -> &PluginMetadata {
556 use std::sync::OnceLock;
557 static META: OnceLock<PluginMetadata> = OnceLock::new();
558 META.get_or_init(|| PluginMetadata::new("panic", "1.0.0", "panic plugin"))
559 }
560 fn ai_extension(&self) -> Option<&dyn super::super::AiExtension> {
561 struct PanicAi;
562 impl super::super::AiExtension for PanicAi {
563 fn name(&self) -> &str {
564 "panic_ai"
565 }
566 fn execute(&self, _input: &str) -> Result<String, PluginError> {
567 panic!("故意 panic")
568 }
569 }
570 Some(&PanicAi)
571 }
572 }
573
574 #[test]
575 fn panic_safe_registry_normal() {
576 let reg = PanicSafeRegistry::new();
577 reg.register_safe(Arc::new(GoodPlugin)).unwrap();
578 let result = reg
579 .invoke_safe("good", |p| {
580 p.metadata();
581 Ok(42i32)
582 })
583 .unwrap();
584 assert_eq!(result, 42);
585 assert_eq!(reg.state("good"), Some(PluginState::Enabled));
586 }
587
588 #[test]
589 fn panic_safe_registry_catches_panic() {
590 let reg = PanicSafeRegistry::new();
591 reg.register_safe(Arc::new(PanicPlugin)).unwrap();
592 let result = reg.invoke_safe("panic", |p| {
593 if let Some(ext) = p.ai_extension() {
594 ext.execute("x")?;
595 }
596 Ok(())
597 });
598 assert!(result.is_err());
599 assert_eq!(reg.state("panic"), Some(PluginState::AutoDisabled));
600 }
601
602 #[test]
603 fn panic_safe_registry_recover() {
604 let reg = PanicSafeRegistry::new();
605 reg.register_safe(Arc::new(PanicPlugin)).unwrap();
606 let _ = reg.invoke_safe("panic", |p| {
607 if let Some(ext) = p.ai_extension() {
608 ext.execute("x")?;
609 }
610 Ok(())
611 });
612 assert_eq!(reg.state("panic"), Some(PluginState::AutoDisabled));
613 reg.enable("panic").unwrap();
614 assert_eq!(reg.state("panic"), Some(PluginState::Enabled));
615 }
616
617 #[test]
618 fn plugin_signer_signed() {
619 let signer = PluginSigner::new(false);
620 let plugin = b"plugin bytes";
621 let key = b"secret key";
622 let sig = signer.sign(plugin, key);
623 let status = signer.verify(plugin, &sig, key);
624 assert_eq!(status, SignatureStatus::Signed);
625 assert!(signer.is_allowed(status));
626 }
627
628 #[test]
629 fn plugin_signer_invalid() {
630 let signer = PluginSigner::new(false);
631 let plugin = b"plugin bytes";
632 let key = b"secret key";
633 let bad_sig = vec![0u8; 32];
634 let status = signer.verify(plugin, &bad_sig, key);
635 assert_eq!(status, SignatureStatus::Invalid);
636 assert!(!signer.is_allowed(status));
637 }
638
639 #[test]
640 fn plugin_signer_unsigned_rejected() {
641 let signer = PluginSigner::new(false);
642 let status = signer.verify(b"plugin", &[], b"key");
643 assert_eq!(status, SignatureStatus::Unsigned);
644 assert!(!signer.is_allowed(status));
645 }
646
647 #[test]
648 fn plugin_signer_unsigned_allowed() {
649 let signer = PluginSigner::new(true);
650 let status = signer.verify(b"plugin", &[], b"key");
651 assert_eq!(status, SignatureStatus::Unsigned);
652 assert!(signer.is_allowed(status));
653 }
654
655 struct NoopMiddleware {
656 name: String,
657 }
658 impl MiddlewareExtension for NoopMiddleware {
659 fn name(&self) -> &str {
660 &self.name
661 }
662 fn before_query(&self, sql: &str) -> Result<String, PluginError> {
663 Ok(sql.to_string())
664 }
665 fn after_query(&self, _sql: &str, result: &str) -> Result<String, PluginError> {
666 Ok(result.to_string())
667 }
668 }
669
670 #[test]
671 fn middleware_chain_order() {
672 let mut chain = MiddlewareChain::new();
673 chain
674 .add(10, Arc::new(NoopMiddleware { name: "a".into() }))
675 .unwrap();
676 chain
677 .add(1, Arc::new(NoopMiddleware { name: "b".into() }))
678 .unwrap();
679 chain
680 .add(5, Arc::new(NoopMiddleware { name: "c".into() }))
681 .unwrap();
682 assert_eq!(chain.len(), 3);
683 let result = chain.before_query("SELECT 1").unwrap();
684 assert_eq!(result, "SELECT 1");
685 }
686
687 #[test]
688 fn middleware_chain_max_len() {
689 let mut chain = MiddlewareChain::new();
690 for i in 0..10 {
691 chain
692 .add(
693 i,
694 Arc::new(NoopMiddleware {
695 name: format!("m{}", i),
696 }),
697 )
698 .unwrap();
699 }
700 assert_eq!(chain.len(), 10);
701 let err = chain
702 .add(
703 100,
704 Arc::new(NoopMiddleware {
705 name: "overflow".into(),
706 }),
707 )
708 .unwrap_err();
709 assert!(matches!(err, PluginError::ChainTooLong(_)));
710 }
711
712 #[test]
713 fn middleware_chain_latency() {
714 let mut chain = MiddlewareChain::new();
715 for i in 0..10 {
716 chain
717 .add(
718 i,
719 Arc::new(NoopMiddleware {
720 name: format!("m{}", i),
721 }),
722 )
723 .unwrap();
724 }
725 chain.before_query("SELECT 1").unwrap();
726 let latency = chain.last_chain_latency();
727 assert!(latency <= Duration::from_millis(1));
728 }
729
730 #[test]
736 fn middleware_chain_overhead_p99_under_1ms() {
737 let mut chain = MiddlewareChain::new();
738 for i in 0..10 {
739 chain
740 .add(
741 i,
742 Arc::new(NoopMiddleware {
743 name: format!("m{}", i),
744 }),
745 )
746 .unwrap();
747 }
748 let sql = "SELECT id, name FROM users WHERE id = 42 AND status = 'active'";
749 let iterations = 10_000usize;
750 let mut latencies: Vec<Duration> = Vec::with_capacity(iterations);
751 for _ in 0..iterations {
752 let start = Instant::now();
753 let out = chain.before_query(sql).unwrap();
754 latencies.push(start.elapsed());
755 assert_eq!(out, sql, "no-op 链不得改写 SQL");
756 }
757 latencies.sort_unstable();
758 let p99 = latencies[iterations * 99 / 100];
759 assert!(
760 p99 <= Duration::from_millis(1),
761 "P99 链开销 {:?} 超过 1ms 声称(10,000 次采样)",
762 p99
763 );
764 }
765
766 #[test]
767 fn middleware_chain_empty() {
768 let chain = MiddlewareChain::new();
769 assert!(chain.is_empty());
770 let result = chain.before_query("SELECT 1").unwrap();
771 assert_eq!(result, "SELECT 1");
772 }
773 }
774}
775
776#[cfg(feature = "composable-plugin")]
777pub use composable::{
778 MiddlewareChain, PanicSafeRegistry, PluginSigner, PluginState, SignatureStatus,
779};