use isoloom_manifest::Manifest;
use crate::BoxFuture;
use crate::name::RunName;
use crate::status::{NetworkView, OutputValue};
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[error("{0}")]
pub struct HookError(String);
impl HookError {
pub fn new(message: impl Into<String>) -> HookError {
HookError(message.into())
}
}
impl From<String> for HookError {
fn from(message: String) -> HookError {
HookError(message)
}
}
impl From<&str> for HookError {
fn from(message: &str) -> HookError {
HookError(message.to_owned())
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct LabView<'a> {
pub name: &'a RunName,
pub manifest: &'a Manifest,
pub networks: &'a [NetworkView],
pub outputs: &'a [OutputValue],
}
pub trait Hooks: Send + Sync {
fn after_up<'a>(&'a self, lab: &'a LabView<'a>) -> BoxFuture<'a, Result<(), HookError>> {
let _ = lab;
Box::pin(async { Ok(()) })
}
fn before_down<'a>(&'a self, lab: &'a LabView<'a>) -> BoxFuture<'a, Result<(), HookError>> {
let _ = lab;
Box::pin(async { Ok(()) })
}
fn before_park<'a>(&'a self, lab: &'a LabView<'a>) -> BoxFuture<'a, Result<(), HookError>> {
let _ = lab;
Box::pin(async { Ok(()) })
}
fn after_resume<'a>(&'a self, lab: &'a LabView<'a>) -> BoxFuture<'a, Result<(), HookError>> {
let _ = lab;
Box::pin(async { Ok(()) })
}
}
const HOOK_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(600);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Edge {
AfterUp,
BeforeDown,
BeforePark,
AfterResume,
}
impl Edge {
fn name(self) -> &'static str {
match self {
Edge::AfterUp => "after_up",
Edge::BeforeDown => "before_down",
Edge::BeforePark => "before_park",
Edge::AfterResume => "after_resume",
}
}
pub(crate) async fn run(self, hooks: &[std::sync::Arc<dyn Hooks>], lab: &LabView<'_>) -> Vec<String> {
let mut failures = Vec::new();
for hook in hooks {
let call = match self {
Edge::AfterUp => hook.after_up(lab),
Edge::BeforeDown => hook.before_down(lab),
Edge::BeforePark => hook.before_park(lab),
Edge::AfterResume => hook.after_resume(lab),
};
match tokio::time::timeout(HOOK_TIMEOUT, call).await {
Ok(Ok(())) => {}
Ok(Err(e)) => failures.push(format!("{}: {e}", self.name())),
Err(_) => failures.push(format!("{}: no answer within {}s", self.name(), HOOK_TIMEOUT.as_secs())),
}
}
failures
}
}