1use chrono::{DateTime, Utc};
12use serde::{Deserialize, Serialize};
13use uuid::Uuid;
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct TaskRequest {
18 #[serde(default = "Uuid::new_v4")]
20 pub id: Uuid,
21 pub skill: String,
23 pub input: serde_json::Value,
25 #[serde(skip_serializing_if = "Option::is_none")]
27 pub caller_agent: Option<String>,
28 #[serde(skip_serializing_if = "Option::is_none")]
30 pub nonce: Option<String>,
31 #[serde(default = "Utc::now")]
33 pub timestamp: DateTime<Utc>,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct TaskResponse {
39 pub id: Uuid,
41 pub status: TaskStatus,
43 #[serde(skip_serializing_if = "Option::is_none")]
45 pub result: Option<serde_json::Value>,
46 #[serde(skip_serializing_if = "Option::is_none")]
48 pub error: Option<String>,
49 #[serde(skip_serializing_if = "Option::is_none")]
51 pub merkle_hash: Option<String>,
52 pub timestamp: DateTime<Utc>,
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
58#[serde(rename_all = "lowercase")]
59pub enum TaskStatus {
60 Pending,
62 Running,
64 Completed,
66 Failed,
68 Cancelled,
70}
71
72impl TaskRequest {
73 pub fn new(skill: impl Into<String>, input: serde_json::Value) -> Self {
75 Self {
76 id: Uuid::new_v4(),
77 skill: skill.into(),
78 input,
79 caller_agent: None,
80 nonce: None,
81 timestamp: Utc::now(),
82 }
83 }
84
85 pub fn with_caller(mut self, agent: impl Into<String>) -> Self {
87 self.caller_agent = Some(agent.into());
88 self
89 }
90
91 pub fn with_nonce(mut self, nonce: impl Into<String>) -> Self {
93 self.nonce = Some(nonce.into());
94 self
95 }
96}
97
98impl TaskResponse {
99 pub fn pending(id: Uuid) -> Self {
101 Self {
102 id,
103 status: TaskStatus::Pending,
104 result: None,
105 error: None,
106 merkle_hash: None,
107 timestamp: Utc::now(),
108 }
109 }
110
111 pub fn completed(id: Uuid, result: serde_json::Value, merkle_hash: impl Into<String>) -> Self {
113 Self {
114 id,
115 status: TaskStatus::Completed,
116 result: Some(result),
117 error: None,
118 merkle_hash: Some(merkle_hash.into()),
119 timestamp: Utc::now(),
120 }
121 }
122
123 pub fn failed(id: Uuid, error: impl Into<String>) -> Self {
125 Self {
126 id,
127 status: TaskStatus::Failed,
128 result: None,
129 error: Some(error.into()),
130 merkle_hash: None,
131 timestamp: Utc::now(),
132 }
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[test]
141 fn test_task_request_new() {
142 let req = TaskRequest::new("verify", serde_json::json!({"claim": "test"}));
143 assert_eq!(req.skill, "verify");
144 assert!(req.caller_agent.is_none());
145 }
146
147 #[test]
148 fn test_task_request_builder() {
149 let req = TaskRequest::new("verify", serde_json::json!({}))
150 .with_caller("other-agent")
151 .with_nonce("abc123");
152
153 assert_eq!(req.caller_agent, Some("other-agent".to_string()));
154 assert_eq!(req.nonce, Some("abc123".to_string()));
155 }
156
157 #[test]
158 fn test_task_response_completed() {
159 let id = Uuid::new_v4();
160 let resp = TaskResponse::completed(id, serde_json::json!({"verified": true}), "hash123");
161
162 assert_eq!(resp.id, id);
163 assert_eq!(resp.status, TaskStatus::Completed);
164 assert!(resp.result.is_some());
165 assert_eq!(resp.merkle_hash, Some("hash123".to_string()));
166 }
167
168 #[test]
169 fn test_task_response_failed() {
170 let id = Uuid::new_v4();
171 let resp = TaskResponse::failed(id, "Verification failed");
172
173 assert_eq!(resp.status, TaskStatus::Failed);
174 assert_eq!(resp.error, Some("Verification failed".to_string()));
175 }
176
177 #[test]
178 fn test_serialization() {
179 let req = TaskRequest::new("hash", serde_json::json!({"text": "hello"}));
180 let json = serde_json::to_string(&req).unwrap();
181 assert!(json.contains("hash"));
182
183 let resp = TaskResponse::pending(req.id);
184 let json = serde_json::to_string(&resp).unwrap();
185 assert!(json.contains("pending"));
186 }
187}