a2a_client/utils/
formatters.rs1use a2a_rs::domain::{Part, TaskState};
4
5pub fn format_task_state(state: &TaskState) -> String {
7 match *state {
8 TaskState::Submitted => "Submitted",
9 TaskState::Working => "Working",
10 TaskState::InputRequired => "Input Required",
11 TaskState::Completed => "Completed",
12 TaskState::Canceled => "Canceled",
13 TaskState::Failed => "Failed",
14 TaskState::Rejected => "Rejected",
15 TaskState::AuthRequired => "Auth Required",
16 TaskState::Unknown => "Unknown",
17 }
18 .to_string()
19}
20
21pub fn format_message_content(parts: &[Part]) -> String {
23 use a2a_rs::domain::part::Content;
24 parts
25 .iter()
26 .map(|part| match &part.content {
27 Some(Content::Text(text)) => text.clone(),
28 Some(Content::Raw(_)) => format!(
29 "[File: {}]",
30 if part.filename.is_empty() {
31 "unnamed"
32 } else {
33 &part.filename
34 }
35 ),
36 Some(Content::Url(url)) => format!(
37 "[File URI: {} ({})]",
38 if part.filename.is_empty() {
39 "unnamed"
40 } else {
41 &part.filename
42 },
43 url
44 ),
45 Some(Content::Data(data)) => {
46 let name = serde_json::to_value(&**data)
47 .ok()
48 .and_then(|v| v.get("name").cloned())
49 .map(|v| {
50 if let serde_json::Value::String(s) = v {
51 s
52 } else {
53 v.to_string()
54 }
55 })
56 .unwrap_or_else(|| "unnamed".to_string());
57 format!("[Data: {}]", name)
58 }
59 None => "[Empty Part]".to_string(),
60 })
61 .collect::<Vec<_>>()
62 .join("\n")
63}
64
65pub fn truncate_preview(text: &str, max_len: usize) -> String {
67 if text.len() <= max_len {
68 text.to_string()
69 } else {
70 format!("{}...", text.chars().take(max_len).collect::<String>())
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77 use a2a_rs::domain::Part;
78
79 #[test]
80 fn test_format_task_state() {
81 assert_eq!(format_task_state(&TaskState::Submitted), "Submitted");
82 assert_eq!(format_task_state(&TaskState::Working), "Working");
83 assert_eq!(
84 format_task_state(&TaskState::InputRequired),
85 "Input Required"
86 );
87 assert_eq!(format_task_state(&TaskState::Completed), "Completed");
88 assert_eq!(format_task_state(&TaskState::Canceled), "Canceled");
89 assert_eq!(format_task_state(&TaskState::Failed), "Failed");
90 assert_eq!(format_task_state(&TaskState::Rejected), "Rejected");
91 assert_eq!(format_task_state(&TaskState::AuthRequired), "Auth Required");
92 assert_eq!(format_task_state(&TaskState::Unknown), "Unknown");
93 }
94
95 #[test]
96 fn test_format_message_content_text() {
97 let parts = vec![Part::text("Hello".to_string())];
98 assert_eq!(format_message_content(&parts), "Hello");
99 }
100
101 #[test]
102 fn test_format_message_content_multiple() {
103 let parts = vec![
104 Part::text("Here is file".to_string()),
105 Part::file_from_bytes(vec![], Some("test.txt".to_string()), None),
106 Part::data({
107 let mut map = serde_json::Map::new();
108 map.insert(
109 "name".to_string(),
110 serde_json::Value::String("metadata".to_string()),
111 );
112 serde_json::from_value(serde_json::Value::Object(map)).unwrap()
113 }),
114 ];
115
116 let expected = "Here is file\n[File: test.txt]\n[Data: metadata]";
117 assert_eq!(format_message_content(&parts), expected);
118 }
119
120 #[test]
121 fn test_truncate_preview() {
122 assert_eq!(truncate_preview("Short", 10), "Short");
123 assert_eq!(truncate_preview("ExactLength", 11), "ExactLength");
124 assert_eq!(truncate_preview("This is too long", 10), "This is to...");
125
126 assert_eq!(truncate_preview("你好世界", 2), "你好...");
128 }
129}