1use serde::{Deserialize, Serialize};
2
3use crate::types::JsonObject;
4
5use super::message::{Artifact, Message};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9#[serde(rename_all = "camelCase")]
10pub struct Task {
11 pub id: String,
13 #[serde(default, skip_serializing_if = "Option::is_none")]
14 pub context_id: Option<String>,
16 pub status: TaskStatus,
18 #[serde(default, skip_serializing_if = "Vec::is_empty")]
19 pub artifacts: Vec<Artifact>,
21 #[serde(default, skip_serializing_if = "Vec::is_empty")]
22 pub history: Vec<Message>,
24 #[serde(default, skip_serializing_if = "Option::is_none")]
25 pub metadata: Option<JsonObject>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31#[serde(rename_all = "camelCase")]
32pub struct TaskStatus {
33 pub state: TaskState,
35 #[serde(default, skip_serializing_if = "Option::is_none")]
36 pub message: Option<Message>,
38 #[serde(default, skip_serializing_if = "Option::is_none")]
39 pub timestamp: Option<String>,
41}
42
43#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
45pub enum TaskState {
46 #[default]
47 #[serde(rename = "TASK_STATE_UNSPECIFIED")]
48 Unspecified,
50 #[serde(rename = "TASK_STATE_SUBMITTED")]
51 Submitted,
53 #[serde(rename = "TASK_STATE_WORKING")]
54 Working,
56 #[serde(rename = "TASK_STATE_COMPLETED")]
57 Completed,
59 #[serde(rename = "TASK_STATE_FAILED")]
60 Failed,
62 #[serde(rename = "TASK_STATE_CANCELED")]
63 Canceled,
65 #[serde(rename = "TASK_STATE_INPUT_REQUIRED")]
66 InputRequired,
68 #[serde(rename = "TASK_STATE_REJECTED")]
69 Rejected,
71 #[serde(rename = "TASK_STATE_AUTH_REQUIRED")]
72 AuthRequired,
74}
75
76#[cfg(test)]
77mod tests {
78 use super::{Task, TaskState, TaskStatus};
79 use crate::types::{Message, Part, Role};
80
81 #[test]
82 fn task_state_serializes_as_proto_enum_name() {
83 let json =
84 serde_json::to_string(&TaskState::Completed).expect("task state should serialize");
85 assert_eq!(json, r#""TASK_STATE_COMPLETED""#);
86 }
87
88 #[test]
89 fn task_round_trip_serialization() {
90 let task = Task {
91 id: "task-1".to_owned(),
92 context_id: Some("ctx-1".to_owned()),
93 status: TaskStatus {
94 state: TaskState::Completed,
95 message: Some(Message {
96 message_id: "msg-1".to_owned(),
97 context_id: Some("ctx-1".to_owned()),
98 task_id: Some("task-1".to_owned()),
99 role: Role::Agent,
100 parts: vec![Part {
101 text: Some("done".to_owned()),
102 raw: None,
103 url: None,
104 data: None,
105 metadata: None,
106 filename: None,
107 media_type: None,
108 }],
109 metadata: None,
110 extensions: Vec::new(),
111 reference_task_ids: Vec::new(),
112 }),
113 timestamp: Some("2026-03-12T12:00:00Z".to_owned()),
114 },
115 artifacts: Vec::new(),
116 history: Vec::new(),
117 metadata: None,
118 };
119
120 let json = serde_json::to_string(&task).expect("task should serialize");
121 let round_trip: Task = serde_json::from_str(&json).expect("task should deserialize");
122
123 assert_eq!(round_trip.id, "task-1");
124 assert_eq!(round_trip.context_id.as_deref(), Some("ctx-1"));
125 assert_eq!(round_trip.status.state, TaskState::Completed);
126 }
127}