uira-core 0.1.1

Shared types, events, protocol definitions, and configuration loading for Uira
Documentation
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub enum HookEvent {
    PreToolUse,
    PostToolUse,
    UserPromptSubmit,
    Stop,
    PreCompact,
    PreCheck,
    PostCheck,
    PreAi,
    PostAi,
    PreFix,
    PostFix,
}

impl HookEvent {
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::PreToolUse => "PreToolUse",
            Self::PostToolUse => "PostToolUse",
            Self::UserPromptSubmit => "UserPromptSubmit",
            Self::Stop => "Stop",
            Self::PreCompact => "PreCompact",
            Self::PreCheck => "pre-check",
            Self::PostCheck => "post-check",
            Self::PreAi => "pre-ai",
            Self::PostAi => "post-ai",
            Self::PreFix => "pre-fix",
            Self::PostFix => "post-fix",
        }
    }

    pub fn is_claude_event(&self) -> bool {
        matches!(
            self,
            Self::PreToolUse
                | Self::PostToolUse
                | Self::UserPromptSubmit
                | Self::Stop
                | Self::PreCompact
        )
    }

    pub fn is_uira_event(&self) -> bool {
        matches!(
            self,
            Self::PreCheck
                | Self::PostCheck
                | Self::PreAi
                | Self::PostAi
                | Self::PreFix
                | Self::PostFix
        )
    }
}

impl std::fmt::Display for HookEvent {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

impl std::str::FromStr for HookEvent {
    type Err = HookEventParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "pretooluse" | "pre-tool-use" | "pre_tool_use" => Ok(Self::PreToolUse),
            "posttooluse" | "post-tool-use" | "post_tool_use" => Ok(Self::PostToolUse),
            "userpromptsubmit" | "user-prompt-submit" | "user_prompt_submit" => {
                Ok(Self::UserPromptSubmit)
            }
            "stop" => Ok(Self::Stop),
            "precompact" | "pre-compact" | "pre_compact" => Ok(Self::PreCompact),
            "precheck" | "pre-check" | "pre_check" => Ok(Self::PreCheck),
            "postcheck" | "post-check" | "post_check" => Ok(Self::PostCheck),
            "preai" | "pre-ai" | "pre_ai" => Ok(Self::PreAi),
            "postai" | "post-ai" | "post_ai" => Ok(Self::PostAi),
            "prefix" | "pre-fix" | "pre_fix" => Ok(Self::PreFix),
            "postfix" | "post-fix" | "post_fix" => Ok(Self::PostFix),
            _ => Err(HookEventParseError(s.to_string())),
        }
    }
}

#[derive(Debug, Clone)]
pub struct HookEventParseError(pub String);

impl std::fmt::Display for HookEventParseError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "unknown hook event: {}", self.0)
    }
}

impl std::error::Error for HookEventParseError {}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_event_as_str() {
        assert_eq!(HookEvent::PreToolUse.as_str(), "PreToolUse");
        assert_eq!(HookEvent::PreCheck.as_str(), "pre-check");
    }

    #[test]
    fn test_event_parse() {
        assert_eq!(
            "PreToolUse".parse::<HookEvent>().unwrap(),
            HookEvent::PreToolUse
        );
        assert_eq!(
            "pre-check".parse::<HookEvent>().unwrap(),
            HookEvent::PreCheck
        );
        assert!("invalid".parse::<HookEvent>().is_err());
    }

    #[test]
    fn test_is_claude_event() {
        assert!(HookEvent::PreToolUse.is_claude_event());
        assert!(!HookEvent::PreCheck.is_claude_event());
    }
}