Skip to main content

a3s_code_core/hooks/
binding.rs

1use std::fmt;
2use std::sync::Arc;
3
4use super::{Hook, HookEventType, HookHandler};
5
6/// Immutable Hook definition and its exact executable handler.
7///
8/// A projected Hook must carry both halves as one value so publication cannot
9/// pair metadata from one generation with a handler from another.
10pub struct HookBinding {
11    hook: Arc<Hook>,
12    handler: Arc<dyn HookHandler>,
13}
14
15impl HookBinding {
16    pub fn new(hook: Hook, handler: Arc<dyn HookHandler>) -> Self {
17        Self {
18            hook: Arc::new(hook),
19            handler,
20        }
21    }
22
23    pub fn hook(&self) -> &Hook {
24        &self.hook
25    }
26
27    pub fn handler(&self) -> &dyn HookHandler {
28        self.handler.as_ref()
29    }
30
31    pub(crate) fn hook_arc(&self) -> &Arc<Hook> {
32        &self.hook
33    }
34
35    pub(crate) fn handler_arc(&self) -> &Arc<dyn HookHandler> {
36        &self.handler
37    }
38
39    pub(crate) fn validate_run_scope(&self) -> Result<(), &'static str> {
40        match self.hook.event_type {
41            HookEventType::SessionStart | HookEventType::SessionEnd => {
42                Err("Session lifecycle Hook events are outside Run-scoped capability projection")
43            }
44            HookEventType::SkillLoad | HookEventType::SkillUnload => {
45                Err("Skill lifecycle Hook events have no Run-scoped production emitter")
46            }
47            _ => Ok(()),
48        }
49    }
50}
51
52impl fmt::Debug for HookBinding {
53    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
54        formatter
55            .debug_struct("HookBinding")
56            .field("hook", &self.hook)
57            .finish_non_exhaustive()
58    }
59}