use crate::error::{CapsulaError, CapsulaResult};
use crate::run::PreparedRun;
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use std::path::PathBuf;
#[derive(Debug, Clone, Default)]
pub struct PreRun;
#[derive(Debug, Clone, Default)]
pub struct PostRun;
pub trait PhaseMarker {
fn phase_name() -> &'static str;
}
impl PhaseMarker for PreRun {
fn phase_name() -> &'static str {
"pre"
}
}
impl PhaseMarker for PostRun {
fn phase_name() -> &'static str {
"post"
}
}
#[derive(Debug, Clone)]
pub struct RuntimeParams<P: PhaseMarker> {
phase_marker: PhantomData<P>,
pub artifact_dir: Option<PathBuf>,
}
impl<P: PhaseMarker> RuntimeParams<P> {
#[must_use]
pub const fn new() -> Self {
Self {
phase_marker: PhantomData,
artifact_dir: None,
}
}
#[must_use]
pub const fn with_artifact_dir(artifact_dir: PathBuf) -> Self {
Self {
phase_marker: PhantomData,
artifact_dir: Some(artifact_dir),
}
}
}
impl<P: PhaseMarker> Default for RuntimeParams<P> {
fn default() -> Self {
Self::new()
}
}
pub trait Hook<P: PhaseMarker>: Send + Sync {
const ID: &'static str;
type Output: super::captured::Captured + 'static;
type Config: Serialize + for<'de> Deserialize<'de>;
fn from_config(
config: &serde_json::Value,
project_root: &std::path::Path,
) -> CapsulaResult<Self>
where
Self: Sized;
fn config(&self) -> &Self::Config;
fn run(&self, metadata: &PreparedRun, params: &RuntimeParams<P>)
-> CapsulaResult<Self::Output>;
fn needs_artifact_dir(&self) -> bool {
false
}
}
pub trait HookErased<P: PhaseMarker>: Send + Sync {
fn id(&self) -> String;
fn config_as_json(&self) -> Result<serde_json::Value, serde_json::Error>;
fn run(
&self,
metadata: &PreparedRun,
params: &RuntimeParams<P>,
) -> Result<Box<dyn super::captured::Captured>, CapsulaError>;
fn needs_artifact_dir(&self) -> bool;
}
impl<T, P> HookErased<P> for T
where
T: Hook<P> + Send + Sync + 'static,
P: PhaseMarker,
{
fn id(&self) -> String {
T::ID.to_string()
}
fn config_as_json(&self) -> Result<serde_json::Value, serde_json::Error> {
serde_json::to_value(<T as Hook<P>>::config(self))
}
fn run(
&self,
metadata: &PreparedRun,
params: &RuntimeParams<P>,
) -> Result<Box<dyn super::captured::Captured>, CapsulaError> {
let out = <T as Hook<P>>::run(self, metadata, params)?;
Ok(Box::new(out))
}
fn needs_artifact_dir(&self) -> bool {
<T as Hook<P>>::needs_artifact_dir(self)
}
}