claude_code_rs/types/
hooks.rs1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use std::future::Future;
4use std::pin::Pin;
5use std::sync::Arc;
6
7#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
9pub enum HookEvent {
10 #[serde(rename = "preToolUse")]
11 PreToolUse,
12 #[serde(rename = "postToolUse")]
13 PostToolUse,
14 #[serde(rename = "notification")]
15 Notification,
16 #[serde(rename = "stop")]
17 Stop,
18 #[serde(rename = "subagentStop")]
19 SubagentStop,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct HookMatcher {
25 #[serde(default, skip_serializing_if = "Option::is_none")]
26 pub tool_name: Option<String>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct PreToolUseInput {
32 pub tool_name: String,
33 pub tool_input: Value,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct PostToolUseInput {
39 pub tool_name: String,
40 pub tool_input: Value,
41 pub tool_output: Value,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct NotificationInput {
47 pub title: String,
48 #[serde(default)]
49 pub message: Option<String>,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct StopInput {
55 #[serde(default)]
56 pub reason: Option<String>,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
61#[serde(tag = "type", rename_all = "camelCase")]
62pub enum HookInput {
63 PreToolUse(PreToolUseInput),
64 PostToolUse(PostToolUseInput),
65 Notification(NotificationInput),
66 Stop(StopInput),
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize, Default)]
71pub struct HookOutput {
72 #[serde(default, skip_serializing_if = "Option::is_none")]
74 pub decision: Option<HookDecision>,
75 #[serde(default, skip_serializing_if = "Option::is_none")]
77 pub reason: Option<String>,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
81#[serde(rename_all = "lowercase")]
82pub enum HookDecision {
83 Approve,
84 Block,
85 Ignore,
86}
87
88impl HookOutput {
89 pub fn approve() -> Self {
90 Self {
91 decision: Some(HookDecision::Approve),
92 reason: None,
93 }
94 }
95
96 pub fn block(reason: impl Into<String>) -> Self {
97 Self {
98 decision: Some(HookDecision::Block),
99 reason: Some(reason.into()),
100 }
101 }
102
103 pub fn ignore() -> Self {
104 Self {
105 decision: Some(HookDecision::Ignore),
106 reason: None,
107 }
108 }
109}
110
111#[derive(Clone)]
113pub struct HookDefinition {
114 pub event: HookEvent,
115 pub matcher: HookMatcher,
116 pub callback: HookCallback,
117}
118
119impl std::fmt::Debug for HookDefinition {
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121 f.debug_struct("HookDefinition")
122 .field("event", &self.event)
123 .field("matcher", &self.matcher)
124 .field("callback", &"<fn>")
125 .finish()
126 }
127}
128
129pub type HookCallback =
131 Arc<dyn Fn(HookInput) -> Pin<Box<dyn Future<Output = HookOutput> + Send>> + Send + Sync>;
132
133pub fn hook_callback<F, Fut>(f: F) -> HookCallback
135where
136 F: Fn(HookInput) -> Fut + Send + Sync + 'static,
137 Fut: Future<Output = HookOutput> + Send + 'static,
138{
139 Arc::new(move |input| Box::pin(f(input)))
140}