Skip to main content

a2a_client/components/
task_viewer.rs

1//! Generic task viewing components
2
3use a2a_rs::domain::{Part as MessagePart, Task};
4use serde::Serialize;
5
6/// View model for a task in a list
7#[derive(Debug, Serialize, Clone)]
8pub struct TaskView {
9    pub task_id: String,
10    pub state: String,
11    pub message_count: usize,
12    pub last_message_preview: Option<String>,
13}
14
15impl TaskView {
16    /// Create a TaskView from an A2A Task
17    pub fn from_task(task: Task) -> Self {
18        let message_count = task.history.as_ref().map(|h| h.len()).unwrap_or(0);
19        let last_message_preview = task.history.as_ref().and_then(|h| {
20            h.last().and_then(|msg| {
21                msg.parts.iter().find_map(|part| match part {
22                    MessagePart::Text { text, .. } => {
23                        Some(text.chars().take(100).collect::<String>())
24                    }
25                    _ => None,
26                })
27            })
28        });
29
30        Self {
31            task_id: task.id,
32            state: format!("{:?}", task.status.state),
33            message_count,
34            last_message_preview,
35        }
36    }
37}
38
39/// View model for a single message
40#[derive(Debug, Serialize, Clone)]
41pub struct MessageView {
42    pub id: String,
43    pub role: String,
44    pub content: String,
45}
46
47impl MessageView {
48    /// Create a MessageView from an A2A Message
49    pub fn from_message(msg: a2a_rs::domain::Message) -> Self {
50        // Extract text content from message parts
51        let content = msg
52            .parts
53            .iter()
54            .map(|part| match part {
55                MessagePart::Text { text, .. } => text.clone(),
56                MessagePart::File { file, .. } => format!(
57                    "[File: {}]",
58                    file.name.as_ref().unwrap_or(&"unnamed".to_string())
59                ),
60                MessagePart::Data { data, .. } => {
61                    let name = data
62                        .get("name")
63                        .and_then(|v| v.as_str())
64                        .unwrap_or("unnamed");
65                    format!("[Data: {}]", name)
66                }
67            })
68            .collect::<Vec<_>>()
69            .join("\n");
70
71        Self {
72            id: msg.message_id,
73            role: format!("{:?}", msg.role),
74            content,
75        }
76    }
77
78    /// Create a MessageView with JSON parsing for structured responses
79    pub fn from_message_with_json_parsing(msg: a2a_rs::domain::Message) -> Self {
80        let content = msg
81            .parts
82            .iter()
83            .filter_map(|part| match part {
84                MessagePart::Text { text, .. } => Some(text.clone()),
85                _ => None,
86            })
87            .collect::<Vec<_>>()
88            .join("\n");
89
90        // Try to parse as JSON for better display
91        let display_content =
92            if let Ok(json_value) = serde_json::from_str::<serde_json::Value>(&content) {
93                if let Some(obj) = json_value.as_object() {
94                    match obj.get("type").and_then(|v| v.as_str()) {
95                        Some("form") => obj
96                            .get("instructions")
97                            .and_then(|v| v.as_str())
98                            .unwrap_or("Please fill out the form.")
99                            .to_string(),
100                        Some("result") => {
101                            let message = obj
102                                .get("message")
103                                .and_then(|v| v.as_str())
104                                .unwrap_or("Request processed.");
105                            let status = obj
106                                .get("status")
107                                .and_then(|v| v.as_str())
108                                .unwrap_or("unknown");
109                            format!("{}\n\nStatus: {}", message, status)
110                        }
111                        _ => serde_json::to_string_pretty(&json_value).unwrap_or(content.clone()),
112                    }
113                } else {
114                    content.clone()
115                }
116            } else {
117                content.clone()
118            };
119
120        Self {
121            id: msg.message_id,
122            role: format!("{:?}", msg.role),
123            content: display_content,
124        }
125    }
126}