use std::fmt;
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum HookEvent {
PreToolUse,
PostToolUse,
UserPromptSubmit,
SessionStart,
Stop,
PreCompact,
PreShellExecution,
}
impl HookEvent {
#[allow(dead_code)]
pub const ALL: &'static [HookEvent] = &[
HookEvent::PreToolUse,
HookEvent::PostToolUse,
HookEvent::UserPromptSubmit,
HookEvent::SessionStart,
HookEvent::Stop,
HookEvent::PreCompact,
HookEvent::PreShellExecution,
];
pub fn as_str(self) -> &'static str {
match self {
HookEvent::PreToolUse => "pre_tool_use",
HookEvent::PostToolUse => "post_tool_use",
HookEvent::UserPromptSubmit => "user_prompt_submit",
HookEvent::SessionStart => "session_start",
HookEvent::Stop => "stop",
HookEvent::PreCompact => "pre_compact",
HookEvent::PreShellExecution => "pre_shell_execution",
}
}
}
impl fmt::Display for HookEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl std::str::FromStr for HookEvent {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
for ev in Self::ALL {
if ev.as_str() == s {
return Ok(*ev);
}
}
Err(format!(
"unknown HookEvent `{s}`; valid: {}",
Self::ALL
.iter()
.map(|e| e.as_str())
.collect::<Vec<_>>()
.join(", ")
))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_variants_have_unique_strings() {
let mut seen = std::collections::HashSet::new();
for event in HookEvent::ALL {
assert!(seen.insert(event.as_str()), "duplicate str for {event:?}");
}
}
#[test]
fn round_trip_through_serde() {
for event in HookEvent::ALL {
let json = serde_json::to_string(event).unwrap();
let parsed: HookEvent = serde_json::from_str(&json).unwrap();
assert_eq!(*event, parsed);
}
}
}