use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::{AtomicUsize, Ordering};
use a2a_protocol_types::error::A2aResult;
use a2a_protocol_types::params::ListTasksParams;
use a2a_protocol_types::responses::TaskListResponse;
use a2a_protocol_types::task::{Task, TaskId, TaskState, TaskStatus};
use crate::store::{ArtifactDelta, InMemoryTaskStore, TaskStore};
type Fut<'a, T> = Pin<Box<dyn Future<Output = A2aResult<T>> + Send + 'a>>;
pub struct StaleReads {
inner: InMemoryTaskStore,
stale: AtomicUsize,
}
impl StaleReads {
pub const fn always(inner: InMemoryTaskStore) -> Self {
Self {
inner,
stale: AtomicUsize::new(usize::MAX),
}
}
pub const fn first(inner: InMemoryTaskStore, n: usize) -> Self {
Self {
inner,
stale: AtomicUsize::new(n),
}
}
}
impl TaskStore for StaleReads {
fn save<'a>(&'a self, task: &'a Task) -> Fut<'a, ()> {
self.inner.save(task)
}
fn save_status_delta<'a>(&'a self, task: &'a Task) -> Fut<'a, ()> {
self.inner.save_status_delta(task)
}
fn save_artifact_delta<'a>(&'a self, task: &'a Task, delta: ArtifactDelta) -> Fut<'a, ()> {
self.inner.save_artifact_delta(task, delta)
}
fn get<'a>(&'a self, id: &'a TaskId) -> Fut<'a, Option<Task>> {
Box::pin(async move {
let mut task = self.inner.get(id).await?;
let mut n = self.stale.load(Ordering::SeqCst);
let stale = loop {
let next = match n {
0 => break false,
usize::MAX => usize::MAX,
n => n - 1,
};
match self
.stale
.compare_exchange_weak(n, next, Ordering::SeqCst, Ordering::SeqCst)
{
Ok(_) => break true,
Err(current) => n = current,
}
};
if stale && let Some(t) = task.as_mut() {
t.status = TaskStatus::new(TaskState::Working);
}
Ok(task)
})
}
fn list<'a>(&'a self, params: &'a ListTasksParams) -> Fut<'a, TaskListResponse> {
self.inner.list(params)
}
fn insert_if_absent<'a>(&'a self, task: &'a Task) -> Fut<'a, bool> {
self.inner.insert_if_absent(task)
}
fn delete<'a>(&'a self, id: &'a TaskId) -> Fut<'a, ()> {
self.inner.delete(id)
}
}