use async_trait::async_trait;
use std::fmt;
#[derive(Debug, Clone)]
pub enum StoreError {
NotFound(String),
StorageError(String),
SerializationError(String),
}
impl fmt::Display for StoreError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StoreError::NotFound(id) => write!(f, "Response not found: {}", id),
StoreError::StorageError(msg) => write!(f, "Storage error: {}", msg),
StoreError::SerializationError(msg) => write!(f, "Serialization error: {}", msg),
}
}
}
impl std::error::Error for StoreError {}
#[async_trait]
pub trait ResponseStore: Send + Sync {
async fn store(&self, response: &serde_json::Value) -> Result<String, StoreError>;
async fn get_context(&self, response_id: &str)
-> Result<Option<serde_json::Value>, StoreError>;
}
#[derive(Debug, Clone, Default)]
pub struct NoOpResponseStore;
#[async_trait]
impl ResponseStore for NoOpResponseStore {
async fn store(&self, _response: &serde_json::Value) -> Result<String, StoreError> {
Ok(format!("noop_{}", uuid_simple()))
}
async fn get_context(
&self,
response_id: &str,
) -> Result<Option<serde_json::Value>, StoreError> {
tracing::debug!(
response_id = %response_id,
"NoOpResponseStore: previous_response_id not supported without a configured store"
);
Ok(None)
}
}
fn uuid_simple() -> String {
use std::time::{SystemTime, UNIX_EPOCH};
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
format!("{:032x}", timestamp)
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_noop_store_returns_none() {
let store = NoOpResponseStore;
let result = store.get_context("any_id").await.unwrap();
assert!(result.is_none());
}
#[tokio::test]
async fn test_noop_store_generates_id() {
let store = NoOpResponseStore;
let response = serde_json::json!({"test": "value"});
let id = store.store(&response).await.unwrap();
assert!(id.starts_with("noop_"));
}
}