protocol_droid/domain/a2a/
task_lifecycle.rs1use std::collections::BTreeMap;
40
41use serde::{Deserialize, Serialize};
42
43use super::task::Task;
44
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
59pub struct GetTaskRequest {
60 pub id: String,
62 #[serde(default, skip_serializing_if = "Option::is_none")]
67 pub history_length: Option<u32>,
68 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
74 pub metadata: BTreeMap<String, serde_json::Value>,
75}
76
77impl GetTaskRequest {
78 #[must_use]
82 pub fn new(id: impl Into<String>) -> Self {
83 Self {
84 id: id.into(),
85 history_length: None,
86 metadata: BTreeMap::new(),
87 }
88 }
89
90 #[must_use]
92 pub const fn with_history_length(mut self, length: u32) -> Self {
93 self.history_length = Some(length);
94 self
95 }
96}
97
98#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
101#[serde(transparent)]
102pub struct GetTaskResponse(pub Task);
103
104impl From<Task> for GetTaskResponse {
105 fn from(task: Task) -> Self {
106 Self(task)
107 }
108}
109
110impl std::ops::Deref for GetTaskResponse {
111 type Target = Task;
112
113 fn deref(&self) -> &Self::Target {
114 &self.0
115 }
116}
117
118#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
127pub struct CancelTaskRequest {
128 pub id: String,
130 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
132 pub metadata: BTreeMap<String, serde_json::Value>,
133}
134
135impl CancelTaskRequest {
136 #[must_use]
138 pub fn new(id: impl Into<String>) -> Self {
139 Self {
140 id: id.into(),
141 metadata: BTreeMap::new(),
142 }
143 }
144}
145
146#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
156#[serde(transparent)]
157pub struct CancelTaskResponse(pub Task);
158
159impl From<Task> for CancelTaskResponse {
160 fn from(task: Task) -> Self {
161 Self(task)
162 }
163}
164
165impl std::ops::Deref for CancelTaskResponse {
166 type Target = Task;
167
168 fn deref(&self) -> &Self::Target {
169 &self.0
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::{CancelTaskRequest, CancelTaskResponse, GetTaskRequest, GetTaskResponse};
176 use crate::domain::a2a::task::{Task, TaskState, TaskStatus};
177
178 type TestError = Box<dyn std::error::Error>;
179
180 #[test]
183 fn get_task_request_serialises_with_id_only() -> Result<(), TestError> {
184 let request = GetTaskRequest::new("task-1");
185 let value = serde_json::to_value(&request)?;
186 let obj = value
187 .as_object()
188 .ok_or("a GetTaskRequest must serialise to a JSON object")?;
189 assert_eq!(
190 obj.get("id").and_then(serde_json::Value::as_str),
191 Some("task-1")
192 );
193 assert!(!obj.contains_key("historyLength"));
197 assert!(!obj.contains_key("metadata"));
198 Ok(())
199 }
200
201 #[test]
202 fn get_task_request_round_trips_with_history_length() -> Result<(), TestError> {
203 let request = GetTaskRequest::new("task-1").with_history_length(5);
204 let json = serde_json::to_string(&request)?;
205 let parsed: GetTaskRequest = serde_json::from_str(&json)?;
206 assert_eq!(parsed, request);
207 Ok(())
208 }
209
210 #[test]
213 fn get_task_response_serialises_as_task_object() -> Result<(), TestError> {
214 let task = Task::new("task-1").with_status(TaskStatus {
215 state: TaskState::Working,
216 message: None,
217 timestamp: None,
218 });
219 let response = GetTaskResponse(task);
220 let value = serde_json::to_value(&response)?;
221 let obj = value
222 .as_object()
223 .ok_or("a GetTaskResponse must serialise to a JSON object")?;
224 assert_eq!(
228 obj.get("id").and_then(serde_json::Value::as_str),
229 Some("task-1")
230 );
231 assert!(obj.contains_key("status"));
232 Ok(())
233 }
234
235 #[test]
236 fn get_task_response_from_task_via_into_works() {
237 let task = Task::new("task-1");
238 let response: GetTaskResponse = task.clone().into();
239 assert_eq!(response.0, task);
240 }
241
242 #[test]
245 fn cancel_task_request_serialises_with_id_only() -> Result<(), TestError> {
246 let request = CancelTaskRequest::new("task-1");
247 let value = serde_json::to_value(&request)?;
248 let obj = value
249 .as_object()
250 .ok_or("a CancelTaskRequest must serialise to a JSON object")?;
251 assert_eq!(
252 obj.get("id").and_then(serde_json::Value::as_str),
253 Some("task-1")
254 );
255 assert!(!obj.contains_key("metadata"));
256 Ok(())
257 }
258
259 #[test]
260 fn cancel_task_request_round_trips() -> Result<(), TestError> {
261 let request = CancelTaskRequest::new("task-1");
262 let json = serde_json::to_string(&request)?;
263 let parsed: CancelTaskRequest = serde_json::from_str(&json)?;
264 assert_eq!(parsed, request);
265 Ok(())
266 }
267
268 #[test]
271 fn cancel_task_response_serialises_cancelled_state() -> Result<(), TestError> {
272 let task = Task::new("task-1").with_status(TaskStatus {
278 state: TaskState::Cancelled,
279 message: None,
280 timestamp: None,
281 });
282 let response = CancelTaskResponse(task);
283 let json = serde_json::to_string(&response)?;
284 let value: serde_json::Value = serde_json::from_str(&json)?;
285 let obj = value
286 .as_object()
287 .ok_or("a CancelTaskResponse must serialise to a JSON object")?;
288 let status = obj
289 .get("status")
290 .and_then(serde_json::Value::as_object)
291 .ok_or("`status` must be a JSON object")?;
292 assert_eq!(
293 status.get("state").and_then(serde_json::Value::as_str),
294 Some("cancelled")
295 );
296 Ok(())
297 }
298}