use std::future::Future;
use std::io;
use std::pin::Pin;
use std::sync::Arc;
use swink_agent::{AgentMessage, CustomMessageRegistry};
use crate::entry::SessionEntry;
use crate::interrupt::InterruptState;
use crate::load_options::LoadOptions;
use crate::meta::SessionMeta;
use crate::search::{SessionHit, SessionSearchOptions};
pub type SessionStoreFuture<'a, T> = Pin<Box<dyn Future<Output = io::Result<T>> + Send + 'a>>;
pub(crate) fn spawn_store_call<T: Send + 'static>(
f: impl FnOnce() -> io::Result<T> + Send + 'static,
) -> SessionStoreFuture<'static, T> {
Box::pin(async move {
tokio::task::spawn_blocking(f)
.await
.map_err(io::Error::other)?
})
}
pub struct BlockingSessionStore<S: crate::store::SessionStore + 'static> {
inner: Arc<S>,
registry: Option<Arc<CustomMessageRegistry>>,
}
impl<S: crate::store::SessionStore + 'static> BlockingSessionStore<S> {
pub fn new(store: S) -> Self {
Self {
inner: Arc::new(store),
registry: None,
}
}
#[must_use]
pub fn with_registry(mut self, registry: Arc<CustomMessageRegistry>) -> Self {
self.registry = Some(registry);
self
}
}
fn clone_messages_for_blocking(messages: &[AgentMessage]) -> Vec<AgentMessage> {
swink_agent::clone_messages_for_send(messages)
}
impl<S: crate::store::SessionStore + 'static> BlockingSessionStore<S> {
pub fn save(
&self,
id: &str,
meta: &SessionMeta,
messages: &[AgentMessage],
) -> SessionStoreFuture<'_, ()> {
let inner = Arc::clone(&self.inner);
let id = id.to_string();
let meta = meta.clone();
let messages = clone_messages_for_blocking(messages);
spawn_store_call(move || inner.save(&id, &meta, &messages))
}
pub fn save_full(
&self,
id: &str,
meta: &SessionMeta,
messages: &[AgentMessage],
state: &serde_json::Value,
) -> SessionStoreFuture<'_, SessionMeta> {
let inner = Arc::clone(&self.inner);
let id = id.to_string();
let meta = meta.clone();
let messages = clone_messages_for_blocking(messages);
let state = state.clone();
spawn_store_call(move || inner.save_full(&id, &meta, &messages, &state))
}
pub fn append(&self, id: &str, messages: &[AgentMessage]) -> SessionStoreFuture<'_, ()> {
let inner = Arc::clone(&self.inner);
let id = id.to_string();
let messages = clone_messages_for_blocking(messages);
spawn_store_call(move || inner.append(&id, &messages))
}
pub fn load(&self, id: &str) -> SessionStoreFuture<'_, (SessionMeta, Vec<AgentMessage>)> {
let inner = Arc::clone(&self.inner);
let id = id.to_string();
let registry = self.registry.clone();
spawn_store_call(move || inner.load(&id, registry.as_deref()))
}
pub fn load_full(
&self,
id: &str,
) -> SessionStoreFuture<'_, (SessionMeta, Vec<AgentMessage>, Option<serde_json::Value>)> {
let inner = Arc::clone(&self.inner);
let id = id.to_string();
let registry = self.registry.clone();
spawn_store_call(move || inner.load_full(&id, registry.as_deref()))
}
pub fn list(&self) -> SessionStoreFuture<'_, Vec<SessionMeta>> {
let inner = Arc::clone(&self.inner);
spawn_store_call(move || inner.list())
}
pub fn delete(&self, id: &str) -> SessionStoreFuture<'_, ()> {
let inner = Arc::clone(&self.inner);
let id = id.to_string();
spawn_store_call(move || inner.delete(&id))
}
pub fn save_state(&self, id: &str, state: &serde_json::Value) -> SessionStoreFuture<'_, ()> {
let inner = Arc::clone(&self.inner);
let id = id.to_string();
let state = state.clone();
spawn_store_call(move || inner.save_state(&id, &state))
}
pub fn load_state(&self, id: &str) -> SessionStoreFuture<'_, Option<serde_json::Value>> {
let inner = Arc::clone(&self.inner);
let id = id.to_string();
spawn_store_call(move || inner.load_state(&id))
}
pub fn save_interrupt(&self, id: &str, state: &InterruptState) -> SessionStoreFuture<'_, ()> {
let inner = Arc::clone(&self.inner);
let id = id.to_string();
let state = state.clone();
spawn_store_call(move || inner.save_interrupt(&id, &state))
}
pub fn load_interrupt(&self, id: &str) -> SessionStoreFuture<'_, Option<InterruptState>> {
let inner = Arc::clone(&self.inner);
let id = id.to_string();
spawn_store_call(move || inner.load_interrupt(&id))
}
pub fn clear_interrupt(&self, id: &str) -> SessionStoreFuture<'_, ()> {
let inner = Arc::clone(&self.inner);
let id = id.to_string();
spawn_store_call(move || inner.clear_interrupt(&id))
}
pub fn load_with_options(
&self,
id: &str,
options: &LoadOptions,
) -> SessionStoreFuture<'_, (SessionMeta, Vec<SessionEntry>)> {
let inner = Arc::clone(&self.inner);
let id = id.to_string();
let options = options.clone();
spawn_store_call(move || inner.load_with_options(&id, &options))
}
pub fn search(
&self,
query: &str,
options: &SessionSearchOptions,
) -> SessionStoreFuture<'_, Vec<SessionHit>> {
let inner = Arc::clone(&self.inner);
let query = query.to_string();
let options = options.clone();
spawn_store_call(move || inner.search(&query, &options))
}
}
#[cfg(test)]
#[path = "store_async_tests.rs"]
mod tests;