1use crate::error::{CapsulaError, CapsulaResult};
2use clap::ValueEnum;
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, Serialize, Deserialize)]
6#[serde(rename_all = "lowercase")]
7pub enum HookPhase {
8 Pre,
9 Post,
10}
11
12#[derive(Debug, Clone)]
13pub struct RuntimeParams {
14 pub phase: HookPhase,
15 pub run_dir: Option<std::path::PathBuf>,
17 pub project_root: std::path::PathBuf,
18}
19
20pub trait Hook {
21 type Output: super::captured::Captured;
22 type Config: Serialize + for<'de> Deserialize<'de>;
23 fn id(&self) -> String;
24 fn config(&self) -> &Self::Config;
25 fn config_as_json(&self) -> Result<serde_json::Value, serde_json::Error> {
26 serde_json::to_value(self.config())
27 }
28 fn run(&self, params: &RuntimeParams) -> CapsulaResult<Self::Output>;
29}
30
31pub trait HookErased: Send + Sync {
33 fn id(&self) -> String;
34 fn config_as_json_erased(&self) -> Result<serde_json::Value, serde_json::Error>;
35 fn run_erased(
36 &self,
37 parmas: &RuntimeParams,
38 ) -> Result<Box<dyn super::captured::Captured>, CapsulaError>;
39}
40
41impl<T> HookErased for T
42where
43 T: Hook + Send + Sync + 'static,
44{
45 fn id(&self) -> String {
46 <T as Hook>::id(self)
47 }
48 fn config_as_json_erased(&self) -> Result<serde_json::Value, serde_json::Error> {
49 self.config_as_json()
50 }
51
52 fn run_erased(
53 &self,
54 params: &RuntimeParams,
55 ) -> Result<Box<dyn super::captured::Captured>, CapsulaError> {
56 let out = <T as Hook>::run(self, params)?;
57 Ok(Box::new(out))
58 }
59}
60
61pub trait HookFactory: Send + Sync {
63 fn key(&self) -> &'static str;
65
66 fn create_hook(
68 &self,
69 config: &serde_json::Value,
70 project_root: &std::path::Path,
71 ) -> CapsulaResult<Box<dyn HookErased>>;
72}