use turul_a2a_proto as pb;
use turul_a2a_types::{Message, Task};
use crate::A2aClientError;
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum SendResponse {
Task(Task),
Message(Message),
}
impl SendResponse {
pub fn task(&self) -> Option<&Task> {
match self {
Self::Task(t) => Some(t),
_ => None,
}
}
pub fn into_task(self) -> Option<Task> {
match self {
Self::Task(t) => Some(t),
_ => None,
}
}
pub fn message(&self) -> Option<&Message> {
match self {
Self::Message(m) => Some(m),
_ => None,
}
}
pub fn into_message(self) -> Option<Message> {
match self {
Self::Message(m) => Some(m),
_ => None,
}
}
pub fn is_task(&self) -> bool {
matches!(self, Self::Task(_))
}
}
impl TryFrom<pb::SendMessageResponse> for SendResponse {
type Error = A2aClientError;
fn try_from(resp: pb::SendMessageResponse) -> Result<Self, Self::Error> {
match resp.payload {
Some(pb::send_message_response::Payload::Task(proto_task)) => {
let task = Task::try_from(proto_task)
.map_err(|e| A2aClientError::Conversion(e.to_string()))?;
Ok(Self::Task(task))
}
Some(pb::send_message_response::Payload::Message(proto_msg)) => {
let msg = Message::try_from(proto_msg)
.map_err(|e| A2aClientError::Conversion(e.to_string()))?;
Ok(Self::Message(msg))
}
None => Err(A2aClientError::Conversion(
"SendMessageResponse has no payload".into(),
)),
}
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct ListResponse {
pub tasks: Vec<Task>,
pub next_page_token: String,
pub total_size: i32,
pub page_size: i32,
}
impl TryFrom<pb::ListTasksResponse> for ListResponse {
type Error = A2aClientError;
fn try_from(resp: pb::ListTasksResponse) -> Result<Self, Self::Error> {
let tasks: Result<Vec<Task>, _> = resp.tasks.into_iter().map(Task::try_from).collect();
let tasks = tasks.map_err(|e| A2aClientError::Conversion(e.to_string()))?;
Ok(Self {
tasks,
next_page_token: resp.next_page_token,
total_size: resp.total_size,
page_size: resp.page_size,
})
}
}
pub fn artifact_text(task: &Task) -> String {
task.artifacts()
.iter()
.flat_map(|a| a.parts.iter())
.filter_map(|p| match &p.content {
Some(pb::part::Content::Text(t)) => Some(t.as_str()),
_ => None,
})
.collect::<Vec<_>>()
.join(" ")
}
pub fn first_data_artifact(task: &Task) -> Option<serde_json::Value> {
for artifact in task.artifacts() {
for part in &artifact.parts {
if let Some(pb::part::Content::Data(proto_struct)) = &part.content {
if let Ok(value) = serde_json::to_value(proto_struct) {
return Some(value);
}
}
}
}
None
}
pub fn first_data_artifact_as<T: serde::de::DeserializeOwned>(
task: &Task,
) -> Option<Result<T, A2aClientError>> {
for artifact in task.artifacts() {
for proto_part in &artifact.parts {
if matches!(&proto_part.content, Some(pb::part::Content::Data(_))) {
let part = turul_a2a_types::Part::from(proto_part.clone());
if let Some(result) = part.parse_data::<T>() {
return Some(result.map_err(|e| A2aClientError::Conversion(e.to_string())));
}
}
}
}
None
}