use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use super::task::{Task, TaskState};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ListTasksRequest {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub context_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub status: Option<TaskState>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub page_size: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub page_token: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub history_length: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub status_timestamp_after: Option<String>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub metadata: BTreeMap<String, serde_json::Value>,
}
impl ListTasksRequest {
#[must_use]
pub const fn new() -> Self {
Self {
context_id: None,
status: None,
page_size: None,
page_token: None,
history_length: None,
status_timestamp_after: None,
metadata: BTreeMap::new(),
}
}
#[must_use]
pub fn with_context_id(mut self, context_id: impl Into<String>) -> Self {
self.context_id = Some(context_id.into());
self
}
#[must_use]
pub const fn with_status(mut self, status: TaskState) -> Self {
self.status = Some(status);
self
}
#[must_use]
pub const fn with_page_size(mut self, page_size: u32) -> Self {
self.page_size = Some(page_size);
self
}
#[must_use]
pub fn with_page_token(mut self, page_token: impl Into<String>) -> Self {
self.page_token = Some(page_token.into());
self
}
#[must_use]
pub const fn with_history_length(mut self, length: u32) -> Self {
self.history_length = Some(length);
self
}
#[must_use]
pub fn with_status_timestamp_after(mut self, timestamp: impl Into<String>) -> Self {
self.status_timestamp_after = Some(timestamp.into());
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ListTasksResponse {
pub tasks: Vec<Task>,
pub next_page_token: String,
pub page_size: u32,
pub total_size: u32,
}
impl ListTasksResponse {
#[must_use]
pub fn new(tasks: impl IntoIterator<Item = Task>) -> Self {
Self {
tasks: tasks.into_iter().collect(),
next_page_token: String::new(),
page_size: 0,
total_size: 0,
}
}
#[must_use]
pub fn with_next_page_token(mut self, next_page_token: impl Into<String>) -> Self {
self.next_page_token = next_page_token.into();
self
}
#[must_use]
pub const fn with_page_size(mut self, page_size: u32) -> Self {
self.page_size = page_size;
self
}
#[must_use]
pub const fn with_total_size(mut self, total_size: u32) -> Self {
self.total_size = total_size;
self
}
}
#[cfg(test)]
mod tests {
use super::{ListTasksRequest, ListTasksResponse};
use crate::domain::a2a::task::{Task, TaskState};
type TestError = Box<dyn std::error::Error>;
#[test]
fn list_request_with_no_fields_is_an_empty_object() -> Result<(), TestError> {
let request = ListTasksRequest::default();
let value = serde_json::to_value(&request)?;
let obj = value
.as_object()
.ok_or("a ListTasksRequest must serialise to a JSON object")?;
assert!(obj.is_empty(), "expected `{{}}`, got {value}");
Ok(())
}
#[test]
fn list_request_round_trips_with_all_filters() -> Result<(), TestError> {
let request = ListTasksRequest::new()
.with_context_id("ctx-1")
.with_status(TaskState::Working)
.with_page_size(50)
.with_page_token("next")
.with_history_length(10)
.with_status_timestamp_after("2025-01-01T00:00:00Z");
let json = serde_json::to_string(&request)?;
let parsed: ListTasksRequest = serde_json::from_str(&json)?;
assert_eq!(parsed, request);
Ok(())
}
#[test]
fn list_response_with_no_next_page_uses_empty_string() -> Result<(), TestError> {
let response = ListTasksResponse::new(Vec::<Task>::new());
let value = serde_json::to_value(&response)?;
let obj = value
.as_object()
.ok_or("a ListTasksResponse must serialise to a JSON object")?;
assert_eq!(
obj.get("nextPageToken").and_then(serde_json::Value::as_str),
Some("")
);
Ok(())
}
#[test]
fn list_response_round_trips_with_tasks_and_pagination() -> Result<(), TestError> {
let response = ListTasksResponse::new([Task::new("task-1"), Task::new("task-2")])
.with_next_page_token("next")
.with_page_size(50)
.with_total_size(123);
let json = serde_json::to_string(&response)?;
let parsed: ListTasksResponse = serde_json::from_str(&json)?;
assert_eq!(parsed, response);
Ok(())
}
}