use serde::Deserialize;
use crate::event::PiEventType;
#[derive(Debug, Clone, Deserialize)]
pub struct RawPiEvent {
pub event: PiEventType,
#[serde(default)]
pub payload: PiPayload,
#[serde(default)]
pub session_id: Option<String>,
#[serde(default)]
pub pid: Option<u32>,
#[serde(default)]
pub tmux_pane: Option<String>,
}
#[derive(Debug, Clone, Default, Deserialize)]
pub struct PiPayload {
#[serde(rename = "toolName", default)]
pub tool_name: Option<String>,
#[serde(rename = "toolCallId", default)]
pub tool_call_id: Option<String>,
#[serde(default)]
pub args: Option<serde_json::Value>,
#[serde(default)]
pub input: Option<serde_json::Value>,
#[serde(default)]
pub result: Option<serde_json::Value>,
#[serde(default)]
pub content: Option<serde_json::Value>,
#[serde(rename = "isError", default)]
pub is_error: Option<bool>,
#[serde(default)]
pub reason: Option<String>,
#[serde(default)]
pub source: Option<String>,
#[serde(default)]
pub text: Option<String>,
#[serde(default)]
pub provider: Option<String>,
#[serde(default)]
pub model: Option<String>,
#[serde(default)]
pub messages: Option<serde_json::Value>,
#[serde(default)]
pub needs_user_input: Option<bool>,
#[serde(default)]
pub title: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_session_start_real_payload() {
let json = r#"{
"event": "session_start",
"payload": {"type": "session_start", "reason": "startup"}
}"#;
let raw: RawPiEvent = serde_json::from_str(json).unwrap();
assert_eq!(raw.event, PiEventType::SessionStart);
assert_eq!(raw.payload.reason.as_deref(), Some("startup"));
}
#[test]
fn parse_input_real_payload() {
let json = r#"{
"event": "input",
"payload": {
"type": "input",
"text": "Use ls to list /tmp",
"source": "interactive"
}
}"#;
let raw: RawPiEvent = serde_json::from_str(json).unwrap();
assert_eq!(raw.event, PiEventType::Input);
assert_eq!(raw.payload.source.as_deref(), Some("interactive"));
assert_eq!(raw.payload.text.as_deref(), Some("Use ls to list /tmp"));
}
#[test]
fn parse_tool_execution_start_real_payload() {
let json = r#"{
"event": "tool_execution_start",
"payload": {
"type": "tool_execution_start",
"toolName": "ls",
"toolCallId": "call_QPdBI",
"args": {"path": "/tmp", "limit": 3}
}
}"#;
let raw: RawPiEvent = serde_json::from_str(json).unwrap();
assert_eq!(raw.event, PiEventType::ToolExecutionStart);
assert_eq!(raw.payload.tool_name.as_deref(), Some("ls"));
assert_eq!(raw.payload.tool_call_id.as_deref(), Some("call_QPdBI"));
}
#[test]
fn parse_tool_call_undeclared_event() {
let json = r#"{
"event": "tool_call",
"payload": {
"type": "tool_call",
"toolName": "ls",
"toolCallId": "call_QPdBI",
"input": {"path": "/tmp"}
}
}"#;
let raw: RawPiEvent = serde_json::from_str(json).unwrap();
assert_eq!(raw.event, PiEventType::ToolCall);
assert_eq!(raw.payload.tool_name.as_deref(), Some("ls"));
assert!(raw.payload.input.is_some());
}
#[test]
fn parse_tool_result_with_error_flag() {
let json = r#"{
"event": "tool_result",
"payload": {
"type": "tool_result",
"toolName": "ls",
"toolCallId": "call_QPdBI",
"isError": false,
"content": [{"type":"text","text":"a\nb\n"}]
}
}"#;
let raw: RawPiEvent = serde_json::from_str(json).unwrap();
assert_eq!(raw.event, PiEventType::ToolResult);
assert_eq!(raw.payload.is_error, Some(false));
}
#[test]
fn parse_session_shutdown_with_reason() {
let json = r#"{
"event": "session_shutdown",
"payload": {"type":"session_shutdown","reason":"quit"}
}"#;
let raw: RawPiEvent = serde_json::from_str(json).unwrap();
assert_eq!(raw.event, PiEventType::SessionShutdown);
assert_eq!(raw.payload.reason.as_deref(), Some("quit"));
}
#[test]
fn parse_unknown_event_falls_back_to_other() {
let json = r#"{"event": "future_pi_event", "payload": {}}"#;
let raw: RawPiEvent = serde_json::from_str(json).unwrap();
assert_eq!(raw.event, PiEventType::Other("future_pi_event".to_string()));
}
}