1use isoloom_manifest::Manifest;
6
7use crate::BoxFuture;
8use crate::name::RunName;
9use crate::status::{NetworkView, OutputValue};
10
11#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
13#[error("{0}")]
14pub struct HookError(String);
15
16impl HookError {
17 pub fn new(message: impl Into<String>) -> HookError {
19 HookError(message.into())
20 }
21}
22
23impl From<String> for HookError {
24 fn from(message: String) -> HookError {
25 HookError(message)
26 }
27}
28
29impl From<&str> for HookError {
30 fn from(message: &str) -> HookError {
31 HookError(message.to_owned())
32 }
33}
34
35#[derive(Debug, Clone)]
37#[non_exhaustive]
38pub struct RunView<'a> {
39 pub name: &'a RunName,
41 pub manifest: &'a Manifest,
43 pub networks: &'a [NetworkView],
45 pub outputs: &'a [OutputValue],
47}
48
49pub trait Hooks: Send + Sync {
51 fn after_up<'a>(&'a self, run: &'a RunView<'a>) -> BoxFuture<'a, Result<(), HookError>> {
53 let _ = run;
54 Box::pin(async { Ok(()) })
55 }
56
57 fn before_down<'a>(&'a self, run: &'a RunView<'a>) -> BoxFuture<'a, Result<(), HookError>> {
59 let _ = run;
60 Box::pin(async { Ok(()) })
61 }
62
63 fn before_park<'a>(&'a self, run: &'a RunView<'a>) -> BoxFuture<'a, Result<(), HookError>> {
65 let _ = run;
66 Box::pin(async { Ok(()) })
67 }
68
69 fn after_resume<'a>(&'a self, run: &'a RunView<'a>) -> BoxFuture<'a, Result<(), HookError>> {
71 let _ = run;
72 Box::pin(async { Ok(()) })
73 }
74}
75
76const HOOK_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(600);
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81pub(crate) enum Edge {
82 AfterUp,
83 BeforeDown,
84 BeforePark,
85 AfterResume,
86}
87
88impl Edge {
89 fn name(self) -> &'static str {
90 match self {
91 Edge::AfterUp => "after_up",
92 Edge::BeforeDown => "before_down",
93 Edge::BeforePark => "before_park",
94 Edge::AfterResume => "after_resume",
95 }
96 }
97
98 pub(crate) async fn run(self, hooks: &[std::sync::Arc<dyn Hooks>], run: &RunView<'_>) -> Vec<String> {
100 let mut failures = Vec::new();
101 for hook in hooks {
102 let call = match self {
103 Edge::AfterUp => hook.after_up(run),
104 Edge::BeforeDown => hook.before_down(run),
105 Edge::BeforePark => hook.before_park(run),
106 Edge::AfterResume => hook.after_resume(run),
107 };
108 match tokio::time::timeout(HOOK_TIMEOUT, call).await {
109 Ok(Ok(())) => {}
110 Ok(Err(e)) => failures.push(format!("{}: {e}", self.name())),
111 Err(_) => failures.push(format!("{}: no answer within {}s", self.name(), HOOK_TIMEOUT.as_secs())),
112 }
113 }
114 failures
115 }
116}