a2a_protocol_server/store/
mod.rs1pub mod retention;
9pub mod task_store;
10pub mod tenant;
11
12#[cfg(any(feature = "sqlite", feature = "postgres"))]
14pub(crate) mod cursor;
15
16pub(crate) mod pagination;
18
19#[cfg(feature = "sqlite")]
20pub mod migration;
21#[cfg(feature = "sqlite")]
22pub mod sqlite_store;
23#[cfg(feature = "sqlite")]
24pub mod tenant_sqlite_store;
25
26#[cfg(feature = "postgres")]
27pub mod pg_migration;
28#[cfg(feature = "postgres")]
29pub mod postgres_store;
30#[cfg(feature = "postgres")]
31pub mod tenant_postgres_store;
32
33#[cfg(any(feature = "sqlite", feature = "postgres"))]
35mod tenant_idempotency;
36
37#[cfg(any(feature = "sqlite", feature = "postgres"))]
39mod event_log_sql;
40#[cfg(any(feature = "sqlite", feature = "postgres"))]
41mod tenant_event_log;
42
43pub use retention::{PurgeReport, RetentionPolicy, terminal_states};
44pub use task_store::{
50 ArtifactDelta, DEFAULT_IDEMPOTENCY_KEY_TTL, DEFAULT_MAX_EVENTS_PER_TASK, DEFAULT_MAX_PAGE_SIZE,
51 IdempotencyClaim, InMemoryTaskStore, RecordedEvent, TaskStore, TaskStoreConfig,
52};
53pub use tenant::{TenantAwareInMemoryTaskStore, TenantContext, TenantStoreConfig};
54
55#[cfg(feature = "sqlite")]
68pub(crate) fn status_timestamp_sqlite(ts: Option<&str>) -> Option<String> {
69 let millis = ts.and_then(a2a_protocol_types::parse_iso8601_to_unix_millis)?;
70 let iso = a2a_protocol_types::unix_millis_to_iso8601(millis);
71 Some(format!("{} {}", &iso[..10], &iso[11..23]))
73}
74
75#[cfg(feature = "postgres")]
80pub(crate) fn status_timestamp_rfc3339(ts: Option<&str>) -> Option<String> {
81 let millis = ts.and_then(a2a_protocol_types::parse_iso8601_to_unix_millis)?;
82 Some(a2a_protocol_types::unix_millis_to_iso8601(millis))
83}
84
85#[cfg(feature = "sqlite")]
86pub use migration::{Migration, MigrationRunner};
87#[cfg(feature = "sqlite")]
88pub use sqlite_store::SqliteTaskStore;
89#[cfg(feature = "sqlite")]
90pub use tenant_sqlite_store::TenantAwareSqliteTaskStore;
91
92#[cfg(feature = "postgres")]
93pub use pg_migration::{BUILTIN_PG_MIGRATIONS, PgMigration, PgMigrationRunner};
94#[cfg(feature = "postgres")]
95pub use postgres_store::PostgresTaskStore;
96#[cfg(feature = "postgres")]
97pub use tenant_postgres_store::TenantAwarePostgresTaskStore;
98
99#[cfg(test)]
100mod status_timestamp_tests {
101 #[cfg(feature = "sqlite")]
108 #[test]
109 fn sqlite_shape_is_the_column_format() {
110 assert_eq!(
111 super::status_timestamp_sqlite(Some("2026-03-15T12:00:00.123Z")).as_deref(),
112 Some("2026-03-15 12:00:00.123"),
113 );
114 assert_eq!(
117 super::status_timestamp_sqlite(Some("2026-03-15T12:00:00Z")).as_deref(),
118 Some("2026-03-15 12:00:00.000"),
119 );
120 }
121
122 #[cfg(feature = "sqlite")]
123 #[test]
124 fn sqlite_returns_none_for_missing_or_unparseable() {
125 assert_eq!(super::status_timestamp_sqlite(None), None);
126 assert_eq!(super::status_timestamp_sqlite(Some("")), None);
127 assert_eq!(
128 super::status_timestamp_sqlite(Some("not a timestamp")),
129 None
130 );
131 assert_eq!(super::status_timestamp_sqlite(Some("2026-03-15")), None);
132 }
133
134 #[cfg(feature = "sqlite")]
135 #[test]
136 fn sqlite_slicing_survives_out_of_range_years() {
137 for input in [
142 "99999-01-01T00:00:00Z",
143 "999999-12-31T23:59:59.999Z",
144 "1969-12-31T23:59:59Z",
145 "0001-01-01T00:00:00Z",
146 "+010000-01-01T00:00:00Z",
147 ] {
148 let out = super::status_timestamp_sqlite(Some(input));
149 if let Some(s) = out {
150 assert!(
151 s.is_char_boundary(0) && s.len() >= 23,
152 "{input} produced a malformed value: {s:?}"
153 );
154 }
155 }
156 }
157
158 #[cfg(feature = "postgres")]
159 #[test]
160 fn rfc3339_normalizes_to_canonical_utc() {
161 assert_eq!(
162 super::status_timestamp_rfc3339(Some("2026-03-15T12:00:00.123Z")).as_deref(),
163 Some("2026-03-15T12:00:00.123Z"),
164 );
165 assert_eq!(
169 super::status_timestamp_rfc3339(Some("2026-03-15T14:00:00+02:00")).as_deref(),
170 Some("2026-03-15T12:00:00.000Z"),
171 );
172 assert_eq!(super::status_timestamp_rfc3339(None), None);
173 assert_eq!(super::status_timestamp_rfc3339(Some("nope")), None);
174 }
175}