pub mod task_store;
pub mod tenant;
#[cfg(any(feature = "sqlite", feature = "postgres"))]
pub(crate) mod cursor;
pub(crate) mod pagination;
#[cfg(feature = "sqlite")]
pub mod migration;
#[cfg(feature = "sqlite")]
pub mod sqlite_store;
#[cfg(feature = "sqlite")]
pub mod tenant_sqlite_store;
#[cfg(feature = "postgres")]
pub mod pg_migration;
#[cfg(feature = "postgres")]
pub mod postgres_store;
#[cfg(feature = "postgres")]
pub mod tenant_postgres_store;
pub use task_store::{InMemoryTaskStore, TaskStore, TaskStoreConfig};
pub use tenant::{TenantAwareInMemoryTaskStore, TenantContext, TenantStoreConfig};
#[cfg(feature = "sqlite")]
pub(crate) fn status_timestamp_sqlite(ts: Option<&str>) -> Option<String> {
let millis = ts.and_then(a2a_protocol_types::parse_iso8601_to_unix_millis)?;
let iso = a2a_protocol_types::unix_millis_to_iso8601(millis);
Some(format!("{} {}", &iso[..10], &iso[11..23]))
}
#[cfg(feature = "postgres")]
pub(crate) fn status_timestamp_rfc3339(ts: Option<&str>) -> Option<String> {
let millis = ts.and_then(a2a_protocol_types::parse_iso8601_to_unix_millis)?;
Some(a2a_protocol_types::unix_millis_to_iso8601(millis))
}
#[cfg(feature = "sqlite")]
pub use migration::{Migration, MigrationRunner};
#[cfg(feature = "sqlite")]
pub use sqlite_store::SqliteTaskStore;
#[cfg(feature = "sqlite")]
pub use tenant_sqlite_store::TenantAwareSqliteTaskStore;
#[cfg(feature = "postgres")]
pub use pg_migration::{PgMigration, PgMigrationRunner};
#[cfg(feature = "postgres")]
pub use postgres_store::PostgresTaskStore;
#[cfg(feature = "postgres")]
pub use tenant_postgres_store::TenantAwarePostgresTaskStore;
#[cfg(test)]
mod status_timestamp_tests {
#[cfg(feature = "sqlite")]
#[test]
fn sqlite_shape_is_the_column_format() {
assert_eq!(
super::status_timestamp_sqlite(Some("2026-03-15T12:00:00.123Z")).as_deref(),
Some("2026-03-15 12:00:00.123"),
);
assert_eq!(
super::status_timestamp_sqlite(Some("2026-03-15T12:00:00Z")).as_deref(),
Some("2026-03-15 12:00:00.000"),
);
}
#[cfg(feature = "sqlite")]
#[test]
fn sqlite_returns_none_for_missing_or_unparseable() {
assert_eq!(super::status_timestamp_sqlite(None), None);
assert_eq!(super::status_timestamp_sqlite(Some("")), None);
assert_eq!(
super::status_timestamp_sqlite(Some("not a timestamp")),
None
);
assert_eq!(super::status_timestamp_sqlite(Some("2026-03-15")), None);
}
#[cfg(feature = "sqlite")]
#[test]
fn sqlite_slicing_survives_out_of_range_years() {
for input in [
"99999-01-01T00:00:00Z",
"999999-12-31T23:59:59.999Z",
"1969-12-31T23:59:59Z",
"0001-01-01T00:00:00Z",
"+010000-01-01T00:00:00Z",
] {
let out = super::status_timestamp_sqlite(Some(input));
if let Some(s) = out {
assert!(
s.is_char_boundary(0) && s.len() >= 23,
"{input} produced a malformed value: {s:?}"
);
}
}
}
#[cfg(feature = "postgres")]
#[test]
fn rfc3339_normalizes_to_canonical_utc() {
assert_eq!(
super::status_timestamp_rfc3339(Some("2026-03-15T12:00:00.123Z")).as_deref(),
Some("2026-03-15T12:00:00.123Z"),
);
assert_eq!(
super::status_timestamp_rfc3339(Some("2026-03-15T14:00:00+02:00")).as_deref(),
Some("2026-03-15T12:00:00.000Z"),
);
assert_eq!(super::status_timestamp_rfc3339(None), None);
assert_eq!(super::status_timestamp_rfc3339(Some("nope")), None);
}
}