1use astro_run::{Context, HookNoopResult, Result};
2use std::sync::Arc;
3
4#[astro_run::async_trait]
5pub trait Plugin: Send + Sync {
6 fn name(&self) -> &'static str;
7 async fn on_before_run(&self, ctx: Context) -> Result<Context> {
8 Ok(ctx)
9 }
10 async fn on_after_run(&self, _ctx: Context) -> HookNoopResult {
11 Ok(())
12 }
13}
14
15pub type SharedPluginDriver = Arc<PluginDriver>;
16
17pub struct PluginDriver {
18 pub(crate) plugins: Vec<Box<dyn Plugin>>,
19}
20
21impl PluginDriver {
22 pub fn new(plugins: Vec<Box<dyn Plugin>>) -> Self {
23 PluginDriver { plugins }
24 }
25
26 pub async fn on_before_run(&self, ctx: Context) -> Context
27 where
28 Self: Send + Sync,
29 {
30 let mut ctx = ctx;
31
32 for plugin in &self.plugins {
33 if let Ok(updated_ctx) = plugin.on_before_run(ctx.clone()).await {
34 ctx = updated_ctx;
35 } else {
36 log::error!("Plugin {} on_before_run error", plugin.name());
37 }
38 }
39
40 ctx
41 }
42
43 pub async fn on_after_run(&self, ctx: Context) {
44 for plugin in &self.plugins {
45 if let Err(err) = plugin.on_after_run(ctx.clone()).await {
46 log::error!("Plugin {} on_after_run error: {}", plugin.name(), err);
47 }
48 }
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use astro_run::StepId;
55
56 use super::*;
57
58 #[astro_run_test::test]
59 async fn test_plugin_driver() {
60 struct TestPlugin;
61
62 #[astro_run::async_trait]
63 impl Plugin for TestPlugin {
64 fn name(&self) -> &'static str {
65 "test"
66 }
67
68 async fn on_before_run(&self, ctx: Context) -> Result<Context> {
69 let mut ctx = ctx;
70
71 ctx.id = StepId::try_from("abc/1/1").unwrap();
72
73 Ok(ctx)
74 }
75
76 async fn on_after_run(&self, _ctx: Context) -> HookNoopResult {
77 Ok(())
78 }
79 }
80
81 struct ErrorBeforeRunPlugin;
82
83 #[astro_run::async_trait]
84 impl Plugin for ErrorBeforeRunPlugin {
85 fn name(&self) -> &'static str {
86 "error-before-run"
87 }
88
89 async fn on_before_run(&self, _ctx: Context) -> Result<Context> {
90 Err(astro_run::Error::error("Error"))
91 }
92 }
93
94 struct ErrorAfterRunPlugin;
95
96 #[astro_run::async_trait]
97 impl Plugin for ErrorAfterRunPlugin {
98 fn name(&self) -> &'static str {
99 "error-before-run"
100 }
101
102 async fn on_after_run(&self, _ctx: Context) -> HookNoopResult {
103 Err(astro_run::Error::error("Error"))
104 }
105 }
106
107 let driver = PluginDriver::new(vec![
108 Box::new(TestPlugin),
109 Box::new(ErrorBeforeRunPlugin),
110 Box::new(ErrorAfterRunPlugin),
111 ]);
112
113 let ctx = astro_run::Context {
114 id: StepId::try_from("aaa/1/1").unwrap(),
115 command: Default::default(),
116 event: None,
117 signal: astro_run::AstroRunSignal::new(),
118 payload: None,
119 };
120
121 let ctx = driver.on_before_run(ctx).await;
122
123 assert_eq!(ctx.id, StepId::try_from("abc/1/1").unwrap());
124
125 driver.on_after_run(ctx).await;
126 }
127}