agent_engine/extensions/
loader.rs1use std::sync::Arc;
8
9use tokio::sync::{mpsc, RwLock};
10
11use super::manager::{ExtensionLoadFailure, ExtensionManager};
12
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum ExtensionLoaderEvent {
15 Started,
16 Loaded {
17 plugin: String,
18 loaded: usize,
19 failed: usize,
20 },
21 Failed {
22 failure: ExtensionLoadFailure,
23 loaded: usize,
24 failed: usize,
25 },
26 Finished {
27 loaded: Vec<String>,
28 failed: Vec<ExtensionLoadFailure>,
29 },
30}
31
32impl ExtensionLoaderEvent {
33 pub fn progress_counts(&self) -> Option<(usize, usize)> {
34 match self {
35 ExtensionLoaderEvent::Started => Some((0, 0)),
36 ExtensionLoaderEvent::Loaded { loaded, failed, .. } => Some((*loaded, *failed)),
37 ExtensionLoaderEvent::Failed { loaded, failed, .. } => Some((*loaded, *failed)),
38 ExtensionLoaderEvent::Finished { loaded, failed } => Some((loaded.len(), failed.len())),
39 }
40 }
41}
42
43pub fn spawn_discover_and_load(
44 manager: Arc<RwLock<ExtensionManager>>,
45 tx: mpsc::UnboundedSender<ExtensionLoaderEvent>,
46) -> tokio::task::JoinHandle<()> {
47 tokio::spawn(async move {
48 let _ = tx.send(ExtensionLoaderEvent::Started);
49 let (loaded, failed) = manager
50 .write()
51 .await
52 .discover_and_load_with_progress(|event| {
53 let _ = tx.send(event);
54 })
55 .await;
56 let _ = tx.send(ExtensionLoaderEvent::Finished { loaded, failed });
57 })
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 #[test]
65 fn progress_counts_report_finished_totals() {
66 let event = ExtensionLoaderEvent::Finished {
67 loaded: vec!["a".into(), "b".into()],
68 failed: vec![ExtensionLoadFailure {
69 plugin: "bad".into(),
70 manifest_path: None,
71 reason: "oops".into(),
72 hint: "fix it".into(),
73 }],
74 };
75 assert_eq!(event.progress_counts(), Some((2, 1)));
76 }
77}