1#![deny(missing_docs)]
29#![deny(unsafe_op_in_unsafe_fn)]
30#![warn(clippy::all, clippy::pedantic, clippy::nursery)]
31#![allow(clippy::module_name_repetitions)]
32
33pub const A2A_VERSION: &str = "1.0.0";
37
38pub const A2A_CONTENT_TYPE: &str = "application/a2a+json";
40
41pub const A2A_VERSION_HEADER: &str = "A2A-Version";
43
44pub mod agent_card;
45pub mod artifact;
46pub mod error;
47pub mod events;
48pub mod extensions;
49pub mod jsonrpc;
50pub mod message;
51pub mod params;
52pub mod push;
53pub mod responses;
54pub mod security;
55pub mod serde_helpers;
56#[cfg(feature = "signing")]
57pub mod signing;
58pub mod task;
59
60pub use agent_card::{AgentCapabilities, AgentCard, AgentInterface, AgentProvider, AgentSkill};
63pub use artifact::{Artifact, ArtifactId};
64pub use error::{A2aError, A2aResult, ErrorCode};
65pub use events::{StreamResponse, TaskArtifactUpdateEvent, TaskStatusUpdateEvent};
66pub use extensions::{AgentCardSignature, AgentExtension};
67pub use jsonrpc::{
68 JsonRpcError, JsonRpcErrorResponse, JsonRpcId, JsonRpcRequest, JsonRpcResponse,
69 JsonRpcSuccessResponse, JsonRpcVersion,
70};
71pub use message::{FileContent, Message, MessageId, MessageRole, Part, PartContent};
72pub use params::{
73 CancelTaskParams, DeletePushConfigParams, GetExtendedAgentCardParams, GetPushConfigParams,
74 ListPushConfigsParams, ListTasksParams, MessageSendParams, SendMessageConfiguration,
75 TaskIdParams, TaskQueryParams,
76};
77pub use push::{AuthenticationInfo, TaskPushNotificationConfig};
78pub use responses::{
79 AuthenticatedExtendedCardResponse, ListPushConfigsResponse, SendMessageResponse,
80 TaskListResponse,
81};
82pub use security::{
83 ApiKeyLocation, ApiKeySecurityScheme, AuthorizationCodeFlow, ClientCredentialsFlow,
84 DeviceCodeFlow, HttpAuthSecurityScheme, ImplicitFlow, MutualTlsSecurityScheme,
85 NamedSecuritySchemes, OAuth2SecurityScheme, OAuthFlows, OpenIdConnectSecurityScheme,
86 PasswordOAuthFlow, SecurityRequirement, SecurityScheme, StringList,
87};
88pub use serde_helpers::{deser_from_slice, deser_from_str, SerBuffer};
89pub use task::{ContextId, Task, TaskId, TaskState, TaskStatus, TaskVersion};
90
91#[must_use]
97pub fn utc_now_iso8601() -> String {
98 use std::time::{SystemTime, UNIX_EPOCH};
99 let secs = SystemTime::now()
100 .duration_since(UNIX_EPOCH)
101 .unwrap_or_default()
102 .as_secs();
103 let (y, m, d, hh, mm, ss) = secs_to_ymd_hms(secs);
105 format!("{y:04}-{m:02}-{d:02}T{hh:02}:{mm:02}:{ss:02}Z")
106}
107
108const fn secs_to_ymd_hms(epoch: u64) -> (u64, u64, u64, u64, u64, u64) {
110 let secs_per_day = 86400_u64;
111 let mut days = epoch / secs_per_day;
112 let time_of_day = epoch % secs_per_day;
113 let hh = time_of_day / 3600;
114 let mm = (time_of_day % 3600) / 60;
115 let ss = time_of_day % 60;
116
117 days += 719_468;
120 let era = days / 146_097;
121 let doe = days - era * 146_097;
122 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146_096) / 365;
123 let y = yoe + era * 400;
124 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
125 let mp = (5 * doy + 2) / 153;
126 let d = doy - (153 * mp + 2) / 5 + 1;
127 let m = if mp < 10 { mp + 3 } else { mp - 9 };
128 let y = if m <= 2 { y + 1 } else { y };
129 (y, m, d, hh, mm, ss)
130}
131
132#[cfg(test)]
133mod tests {
134 use super::secs_to_ymd_hms;
135
136 #[test]
141 fn epoch_zero() {
142 assert_eq!(secs_to_ymd_hms(0), (1970, 1, 1, 0, 0, 0));
143 }
144
145 #[test]
147 fn time_of_day_decomposition() {
148 assert_eq!(secs_to_ymd_hms(3723), (1970, 1, 1, 1, 2, 3));
150 assert_eq!(secs_to_ymd_hms(86399), (1970, 1, 1, 23, 59, 59));
152 }
153
154 #[test]
156 fn day_boundary() {
157 assert_eq!(secs_to_ymd_hms(86400), (1970, 1, 2, 0, 0, 0));
159 }
160
161 #[test]
163 fn known_date_2000_01_01() {
164 assert_eq!(secs_to_ymd_hms(946_684_800), (2000, 1, 1, 0, 0, 0));
166 }
167
168 #[test]
169 fn known_date_leap_day_2000() {
170 assert_eq!(secs_to_ymd_hms(951_782_400), (2000, 2, 29, 0, 0, 0));
172 }
173
174 #[test]
175 fn known_date_2024_02_29() {
176 assert_eq!(secs_to_ymd_hms(1_709_164_800), (2024, 2, 29, 0, 0, 0));
178 }
179
180 #[test]
181 fn known_date_2024_03_01() {
182 assert_eq!(secs_to_ymd_hms(1_709_251_200), (2024, 3, 1, 0, 0, 0));
184 }
185
186 #[test]
188 fn january_february_year_adjustment() {
189 assert_eq!(secs_to_ymd_hms(1_767_225_600), (2026, 1, 1, 0, 0, 0));
191 assert_eq!(secs_to_ymd_hms(1_772_236_800), (2026, 2, 28, 0, 0, 0));
193 }
194
195 #[test]
197 fn march_mp_boundary() {
198 assert_eq!(secs_to_ymd_hms(1_772_323_200), (2026, 3, 1, 0, 0, 0));
200 assert_eq!(secs_to_ymd_hms(1_767_225_599), (2025, 12, 31, 23, 59, 59));
202 }
203
204 #[test]
206 fn era_boundary_1600() {
207 assert_eq!(secs_to_ymd_hms(978_307_200), (2001, 1, 1, 0, 0, 0));
210 }
211
212 #[test]
214 fn non_leap_century() {
215 assert_eq!(secs_to_ymd_hms(5_097_600), (1970, 3, 1, 0, 0, 0));
217 }
218
219 #[test]
221 fn full_timestamp_2026_03_15() {
222 assert_eq!(secs_to_ymd_hms(1_773_585_045), (2026, 3, 15, 14, 30, 45));
224 }
225
226 #[test]
228 fn end_of_year() {
229 assert_eq!(secs_to_ymd_hms(1_767_139_200), (2025, 12, 31, 0, 0, 0));
231 }
232
233 #[test]
235 fn mid_year_date() {
236 assert_eq!(secs_to_ymd_hms(1_686_830_400), (2023, 6, 15, 12, 0, 0));
238 }
239}