use super::traits::{Event, EventType};
use serde::{Deserialize, Serialize};
use std::time::{SystemTime, UNIX_EPOCH};
fn generate_id() -> String {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
format!("evt_{:x}", now)
}
fn now_ms() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as u64
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCall {
pub id: String,
pub name: String,
pub arguments: serde_json::Value,
}
impl ToolCall {
pub fn new(id: impl Into<String>, name: impl Into<String>, arguments: serde_json::Value) -> Self {
Self {
id: id.into(),
name: name.into(),
arguments,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ToolCallStatus {
Pending,
Executing,
Completed,
Failed,
Cancelled,
}
impl Default for ToolCallStatus {
fn default() -> Self {
Self::Pending
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpContext {
pub server_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub server_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub transport: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCallEvent {
pub event_id: String,
pub session_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub project_hash: Option<String>,
pub timestamp_ms: u64,
pub sequence: u32,
pub message_event_id: String,
pub tool_call: ToolCall,
#[serde(default)]
pub status: ToolCallStatus,
#[serde(skip_serializing_if = "Option::is_none")]
pub mcp_context: Option<McpContext>,
}
impl ToolCallEvent {
pub fn new(
session_id: impl Into<String>,
sequence: u32,
message_event_id: impl Into<String>,
tool_call: ToolCall,
) -> Self {
Self {
event_id: generate_id(),
session_id: session_id.into(),
project_hash: None,
timestamp_ms: now_ms(),
sequence,
message_event_id: message_event_id.into(),
tool_call,
status: ToolCallStatus::Pending,
mcp_context: None,
}
}
pub fn with_project(mut self, project_hash: impl Into<String>) -> Self {
self.project_hash = Some(project_hash.into());
self
}
pub fn with_mcp_context(mut self, ctx: McpContext) -> Self {
self.mcp_context = Some(ctx);
self
}
pub fn with_status(mut self, status: ToolCallStatus) -> Self {
self.status = status;
self
}
pub fn with_event_id(mut self, event_id: impl Into<String>) -> Self {
self.event_id = event_id.into();
self
}
}
impl Event for ToolCallEvent {
fn event_id(&self) -> &str {
&self.event_id
}
fn event_type(&self) -> EventType {
EventType::ToolCall
}
fn session_id(&self) -> &str {
&self.session_id
}
fn timestamp_ms(&self) -> u64 {
self.timestamp_ms
}
fn sequence(&self) -> u32 {
self.sequence
}
fn to_json(&self) -> serde_json::Value {
serde_json::to_value(self).unwrap()
}
}