use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use super::task::Task;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GetTaskRequest {
pub id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub history_length: Option<u32>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub metadata: BTreeMap<String, serde_json::Value>,
}
impl GetTaskRequest {
#[must_use]
pub fn new(id: impl Into<String>) -> Self {
Self {
id: id.into(),
history_length: None,
metadata: BTreeMap::new(),
}
}
#[must_use]
pub const fn with_history_length(mut self, length: u32) -> Self {
self.history_length = Some(length);
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct GetTaskResponse(pub Task);
impl From<Task> for GetTaskResponse {
fn from(task: Task) -> Self {
Self(task)
}
}
impl std::ops::Deref for GetTaskResponse {
type Target = Task;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CancelTaskRequest {
pub id: String,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub metadata: BTreeMap<String, serde_json::Value>,
}
impl CancelTaskRequest {
#[must_use]
pub fn new(id: impl Into<String>) -> Self {
Self {
id: id.into(),
metadata: BTreeMap::new(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct CancelTaskResponse(pub Task);
impl From<Task> for CancelTaskResponse {
fn from(task: Task) -> Self {
Self(task)
}
}
impl std::ops::Deref for CancelTaskResponse {
type Target = Task;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(test)]
mod tests {
use super::{CancelTaskRequest, CancelTaskResponse, GetTaskRequest, GetTaskResponse};
use crate::domain::a2a::task::{Task, TaskState, TaskStatus};
type TestError = Box<dyn std::error::Error>;
#[test]
fn get_task_request_serialises_with_id_only() -> Result<(), TestError> {
let request = GetTaskRequest::new("task-1");
let value = serde_json::to_value(&request)?;
let obj = value
.as_object()
.ok_or("a GetTaskRequest must serialise to a JSON object")?;
assert_eq!(
obj.get("id").and_then(serde_json::Value::as_str),
Some("task-1")
);
assert!(!obj.contains_key("historyLength"));
assert!(!obj.contains_key("metadata"));
Ok(())
}
#[test]
fn get_task_request_round_trips_with_history_length() -> Result<(), TestError> {
let request = GetTaskRequest::new("task-1").with_history_length(5);
let json = serde_json::to_string(&request)?;
let parsed: GetTaskRequest = serde_json::from_str(&json)?;
assert_eq!(parsed, request);
Ok(())
}
#[test]
fn get_task_response_serialises_as_task_object() -> Result<(), TestError> {
let task = Task::new("task-1").with_status(TaskStatus {
state: TaskState::Working,
message: None,
timestamp: None,
});
let response = GetTaskResponse(task);
let value = serde_json::to_value(&response)?;
let obj = value
.as_object()
.ok_or("a GetTaskResponse must serialise to a JSON object")?;
assert_eq!(
obj.get("id").and_then(serde_json::Value::as_str),
Some("task-1")
);
assert!(obj.contains_key("status"));
Ok(())
}
#[test]
fn get_task_response_from_task_via_into_works() {
let task = Task::new("task-1");
let response: GetTaskResponse = task.clone().into();
assert_eq!(response.0, task);
}
#[test]
fn cancel_task_request_serialises_with_id_only() -> Result<(), TestError> {
let request = CancelTaskRequest::new("task-1");
let value = serde_json::to_value(&request)?;
let obj = value
.as_object()
.ok_or("a CancelTaskRequest must serialise to a JSON object")?;
assert_eq!(
obj.get("id").and_then(serde_json::Value::as_str),
Some("task-1")
);
assert!(!obj.contains_key("metadata"));
Ok(())
}
#[test]
fn cancel_task_request_round_trips() -> Result<(), TestError> {
let request = CancelTaskRequest::new("task-1");
let json = serde_json::to_string(&request)?;
let parsed: CancelTaskRequest = serde_json::from_str(&json)?;
assert_eq!(parsed, request);
Ok(())
}
#[test]
fn cancel_task_response_serialises_cancelled_state() -> Result<(), TestError> {
let task = Task::new("task-1").with_status(TaskStatus {
state: TaskState::Cancelled,
message: None,
timestamp: None,
});
let response = CancelTaskResponse(task);
let json = serde_json::to_string(&response)?;
let value: serde_json::Value = serde_json::from_str(&json)?;
let obj = value
.as_object()
.ok_or("a CancelTaskResponse must serialise to a JSON object")?;
let status = obj
.get("status")
.and_then(serde_json::Value::as_object)
.ok_or("`status` must be a JSON object")?;
assert_eq!(
status.get("state").and_then(serde_json::Value::as_str),
Some("cancelled")
);
Ok(())
}
}