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]
94 pub const fn with_history_length(mut self, length: u32) -> Self {
95 self.history_length = Some(length);
96 self
97 }
98}
99
100#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
103#[serde(transparent)]
104pub struct GetTaskResponse(pub Task);
105
106impl From<Task> for GetTaskResponse {
107 fn from(task: Task) -> Self {
108 Self(task)
109 }
110}
111
112impl std::ops::Deref for GetTaskResponse {
113 type Target = Task;
114
115 fn deref(&self) -> &Self::Target {
116 &self.0
117 }
118}
119
120#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
129pub struct CancelTaskRequest {
130 pub id: String,
132 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
134 pub metadata: BTreeMap<String, serde_json::Value>,
135}
136
137impl CancelTaskRequest {
138 #[must_use]
140 pub fn new(id: impl Into<String>) -> Self {
141 Self {
142 id: id.into(),
143 metadata: BTreeMap::new(),
144 }
145 }
146}
147
148#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
158#[serde(transparent)]
159pub struct CancelTaskResponse(pub Task);
160
161impl From<Task> for CancelTaskResponse {
162 fn from(task: Task) -> Self {
163 Self(task)
164 }
165}
166
167impl std::ops::Deref for CancelTaskResponse {
168 type Target = Task;
169
170 fn deref(&self) -> &Self::Target {
171 &self.0
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use super::{CancelTaskRequest, CancelTaskResponse, GetTaskRequest, GetTaskResponse};
178 use crate::domain::a2a::task::{Task, TaskState, TaskStatus};
179
180 type TestError = Box<dyn std::error::Error>;
181
182 #[test]
185 fn get_task_request_serialises_with_id_only() -> Result<(), TestError> {
186 let request = GetTaskRequest::new("task-1");
187 let value = serde_json::to_value(&request)?;
188 let obj = value
189 .as_object()
190 .ok_or("a GetTaskRequest must serialise to a JSON object")?;
191 assert_eq!(
192 obj.get("id").and_then(serde_json::Value::as_str),
193 Some("task-1")
194 );
195 assert!(!obj.contains_key("historyLength"));
199 assert!(!obj.contains_key("metadata"));
200 Ok(())
201 }
202
203 #[test]
204 fn get_task_request_round_trips_with_history_length() -> Result<(), TestError> {
205 let request = GetTaskRequest::new("task-1").with_history_length(5);
206 let json = serde_json::to_string(&request)?;
207 let parsed: GetTaskRequest = serde_json::from_str(&json)?;
208 assert_eq!(parsed, request);
209 Ok(())
210 }
211
212 #[test]
215 fn get_task_response_serialises_as_task_object() -> Result<(), TestError> {
216 let task = Task::new("task-1").with_status(TaskStatus {
217 state: TaskState::Working,
218 message: None,
219 timestamp: None,
220 });
221 let response = GetTaskResponse(task);
222 let value = serde_json::to_value(&response)?;
223 let obj = value
224 .as_object()
225 .ok_or("a GetTaskResponse must serialise to a JSON object")?;
226 assert_eq!(
230 obj.get("id").and_then(serde_json::Value::as_str),
231 Some("task-1")
232 );
233 assert!(obj.contains_key("status"));
234 Ok(())
235 }
236
237 #[test]
238 fn get_task_response_from_task_via_into_works() {
239 let task = Task::new("task-1");
240 let response: GetTaskResponse = task.clone().into();
241 assert_eq!(response.0, task);
242 }
243
244 #[test]
247 fn cancel_task_request_serialises_with_id_only() -> Result<(), TestError> {
248 let request = CancelTaskRequest::new("task-1");
249 let value = serde_json::to_value(&request)?;
250 let obj = value
251 .as_object()
252 .ok_or("a CancelTaskRequest must serialise to a JSON object")?;
253 assert_eq!(
254 obj.get("id").and_then(serde_json::Value::as_str),
255 Some("task-1")
256 );
257 assert!(!obj.contains_key("metadata"));
258 Ok(())
259 }
260
261 #[test]
262 fn cancel_task_request_round_trips() -> Result<(), TestError> {
263 let request = CancelTaskRequest::new("task-1");
264 let json = serde_json::to_string(&request)?;
265 let parsed: CancelTaskRequest = serde_json::from_str(&json)?;
266 assert_eq!(parsed, request);
267 Ok(())
268 }
269
270 #[test]
273 fn cancel_task_response_serialises_cancelled_state() -> Result<(), TestError> {
274 let task = Task::new("task-1").with_status(TaskStatus {
280 state: TaskState::Cancelled,
281 message: None,
282 timestamp: None,
283 });
284 let response = CancelTaskResponse(task);
285 let json = serde_json::to_string(&response)?;
286 let value: serde_json::Value = serde_json::from_str(&json)?;
287 let obj = value
288 .as_object()
289 .ok_or("a CancelTaskResponse must serialise to a JSON object")?;
290 let status = obj
291 .get("status")
292 .and_then(serde_json::Value::as_object)
293 .ok_or("`status` must be a JSON object")?;
294 assert_eq!(
295 status.get("state").and_then(serde_json::Value::as_str),
296 Some("cancelled")
297 );
298 Ok(())
299 }
300}