a2a_rust/types/
agent_id.rs1use std::fmt;
2use std::str::FromStr;
3
4use serde::{Deserialize, Serialize};
5
6use crate::A2AError;
7
8#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
16#[serde(transparent)]
17pub struct AgentId(String);
18
19impl AgentId {
20 pub fn new(value: impl Into<String>) -> Result<Self, A2AError> {
22 let value = value.into();
23 validate_agent_id(&value)?;
24 Ok(Self(value))
25 }
26
27 pub fn as_str(&self) -> &str {
29 &self.0
30 }
31}
32
33impl AsRef<str> for AgentId {
34 fn as_ref(&self) -> &str {
35 self.as_str()
36 }
37}
38
39impl fmt::Display for AgentId {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 self.0.fmt(f)
42 }
43}
44
45impl From<AgentId> for String {
46 fn from(value: AgentId) -> Self {
47 value.0
48 }
49}
50
51impl TryFrom<String> for AgentId {
52 type Error = A2AError;
53
54 fn try_from(value: String) -> Result<Self, Self::Error> {
55 Self::new(value)
56 }
57}
58
59impl TryFrom<&str> for AgentId {
60 type Error = A2AError;
61
62 fn try_from(value: &str) -> Result<Self, Self::Error> {
63 Self::new(value)
64 }
65}
66
67impl FromStr for AgentId {
68 type Err = A2AError;
69
70 fn from_str(s: &str) -> Result<Self, Self::Err> {
71 Self::new(s)
72 }
73}
74
75impl<'de> Deserialize<'de> for AgentId {
76 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
77 where
78 D: serde::Deserializer<'de>,
79 {
80 let value = String::deserialize(deserializer)?;
81 Self::new(value).map_err(serde::de::Error::custom)
82 }
83}
84
85fn validate_agent_id(value: &str) -> Result<(), A2AError> {
86 if !(3..=64).contains(&value.len()) {
87 return Err(A2AError::InvalidRequest(
88 "agent_id must be between 3 and 64 characters".to_owned(),
89 ));
90 }
91
92 if !value
93 .bytes()
94 .all(|byte| byte.is_ascii_lowercase() || byte.is_ascii_digit() || byte == b'-')
95 {
96 return Err(A2AError::InvalidRequest(
97 "agent_id may only contain lowercase ASCII letters, digits, and '-'".to_owned(),
98 ));
99 }
100
101 Ok(())
102}
103
104#[cfg(test)]
105mod tests {
106 use super::AgentId;
107
108 #[test]
109 fn agent_id_accepts_valid_values() {
110 let agent_id = AgentId::new("echo-agent-01").expect("agent id should validate");
111 assert_eq!(agent_id.as_str(), "echo-agent-01");
112 }
113
114 #[test]
115 fn agent_id_rejects_invalid_characters() {
116 let error = AgentId::new("Echo_Agent").expect_err("agent id should be invalid");
117 assert!(
118 error
119 .to_string()
120 .contains("lowercase ASCII letters, digits, and '-'")
121 );
122 }
123
124 #[test]
125 fn agent_id_rejects_invalid_length() {
126 let error = AgentId::new("ab").expect_err("agent id should be invalid");
127 assert!(error.to_string().contains("between 3 and 64 characters"));
128 }
129}