use serde::{Deserialize, Serialize};
use crate::agent_card::AgentCard;
use crate::message::Message;
use crate::task::Task;
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum SendMessageResponse {
Task(Task),
Message(Message),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TaskListResponse {
pub tasks: Vec<Task>,
#[serde(default)]
pub next_page_token: String,
#[serde(default)]
pub page_size: u32,
#[serde(default)]
pub total_size: u32,
}
impl TaskListResponse {
#[must_use]
#[allow(clippy::missing_const_for_fn)] pub fn new(tasks: Vec<Task>) -> Self {
#[allow(clippy::cast_possible_truncation)]
let total = tasks.len() as u32;
Self {
page_size: total,
total_size: total,
tasks,
next_page_token: String::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ListPushConfigsResponse {
pub configs: Vec<crate::push::TaskPushNotificationConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_page_token: Option<String>,
}
pub type AuthenticatedExtendedCardResponse = AgentCard;
#[cfg(test)]
mod tests {
use super::*;
use crate::message::{MessageId, MessageRole, Part};
use crate::task::{ContextId, TaskId, TaskState, TaskStatus};
fn make_task() -> Task {
Task {
id: TaskId::new("t1"),
context_id: ContextId::new("c1"),
status: TaskStatus::new(TaskState::Completed),
history: None,
artifacts: None,
metadata: None,
}
}
fn make_message() -> Message {
Message {
id: MessageId::new("m1"),
role: MessageRole::Agent,
parts: vec![Part::text("hi")],
task_id: None,
context_id: None,
reference_task_ids: None,
extensions: None,
metadata: None,
}
}
#[test]
fn send_message_response_task_variant() {
let resp = SendMessageResponse::Task(make_task());
let json = serde_json::to_string(&resp).expect("serialize");
assert!(
json.contains("\"task\""),
"v1.0 should have 'task' wrapper key: {json}"
);
let back: SendMessageResponse = serde_json::from_str(&json).expect("deserialize");
match &back {
SendMessageResponse::Task(t) => {
assert_eq!(t.id, TaskId::new("t1"));
assert_eq!(t.status.state, TaskState::Completed);
}
_ => panic!("expected Task variant"),
}
}
#[test]
fn send_message_response_message_variant() {
let resp = SendMessageResponse::Message(make_message());
let json = serde_json::to_string(&resp).expect("serialize");
assert!(
json.contains("\"message\""),
"v1.0 should have 'message' wrapper key: {json}"
);
let back: SendMessageResponse = serde_json::from_str(&json).expect("deserialize");
match &back {
SendMessageResponse::Message(m) => {
assert_eq!(m.id, MessageId::new("m1"));
assert_eq!(m.role, MessageRole::Agent);
}
_ => panic!("expected Message variant"),
}
}
#[test]
fn send_message_response_deserialize_task() {
let json = serde_json::json!({
"task": {
"id": "t1",
"contextId": "c1",
"status": {"state": "TASK_STATE_COMPLETED"}
}
});
let back: SendMessageResponse =
serde_json::from_value(json).expect("should deserialize as Task");
match back {
SendMessageResponse::Task(task) => {
assert_eq!(task.id.as_ref(), "t1");
assert_eq!(task.context_id.as_ref(), "c1");
}
other => panic!("expected Task variant, got {other:?}"),
}
}
#[test]
fn send_message_response_deserialize_message() {
let json = serde_json::json!({
"message": {
"messageId": "m1",
"role": "ROLE_AGENT",
"parts": [{ "text": "hi" }]
}
});
let resp: SendMessageResponse =
serde_json::from_value(json).expect("should deserialize as Message");
assert!(
matches!(resp, SendMessageResponse::Message(_)),
"expected Message variant"
);
}
#[test]
fn task_list_response_roundtrip() {
let resp = TaskListResponse {
tasks: vec![make_task()],
next_page_token: "cursor-abc".into(),
page_size: 10,
total_size: 1,
};
let json = serde_json::to_string(&resp).expect("serialize");
assert!(json.contains("\"nextPageToken\":\"cursor-abc\""));
let back: TaskListResponse = serde_json::from_str(&json).expect("deserialize");
assert_eq!(back.tasks.len(), 1);
assert_eq!(back.next_page_token, "cursor-abc");
}
#[test]
fn task_list_response_empty_always_includes_required_fields() {
let resp = TaskListResponse::new(vec![]);
let json = serde_json::to_string(&resp).expect("serialize");
assert!(
json.contains("\"nextPageToken\""),
"nextPageToken must always be present: {json}"
);
assert!(
json.contains("\"pageSize\""),
"pageSize must always be present: {json}"
);
assert!(
json.contains("\"totalSize\""),
"totalSize must always be present: {json}"
);
}
}