use std::collections::HashMap;
use std::future::Future;
use buffa::Message;
use bytes::Bytes;
use exoware_sdk::common::kv::v1::Entry;
use exoware_sdk::log::stream::v1::GetResponse as StreamGetResponse;
use exoware_sdk::prune_policy::PrunePolicyDocument;
pub type QueryExtra = HashMap<String, buffa_types::google::protobuf::Value>;
#[derive(Clone, Debug, Default)]
pub struct RangeScanBatch {
pub rows: Vec<(Bytes, Bytes)>,
pub extra: QueryExtra,
}
pub trait RangeScan: Send {
fn next_batch(
&mut self,
max_items: usize,
) -> impl Future<Output = Result<RangeScanBatch, String>> + Send;
}
pub trait Sequence: Send + Sync + 'static {
fn current_sequence(&self) -> u64;
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum IngestError {
#[error("unavailable: {message}")]
Unavailable { message: String },
#[error("internal: {message}")]
Internal { message: String },
}
pub trait Ingest: Send + Sync + 'static {
fn put_batch(
&self,
kvs: Vec<(Bytes, Bytes)>,
) -> impl Future<Output = Result<u64, IngestError>> + Send;
}
pub trait Query: Sequence {
type RangeScan: RangeScan + 'static;
fn get(
&self,
key: Bytes,
) -> impl Future<Output = Result<(Option<Bytes>, QueryExtra), String>> + Send;
fn range_scan(
&self,
start: Bytes,
end: Bytes,
limit: usize,
forward: bool,
) -> impl Future<Output = Result<Self::RangeScan, String>> + Send;
fn get_many(
&self,
keys: Vec<Bytes>,
) -> impl Future<Output = Result<(Vec<(Bytes, Option<Bytes>)>, QueryExtra), String>> + Send;
}
pub trait Prune: Send + Sync + 'static {
fn apply_prune_policies(
&self,
document: PrunePolicyDocument,
) -> impl Future<Output = Result<(), String>> + Send;
}
#[derive(Clone, Debug)]
pub struct LogBatch {
sequence_number: u64,
response_bytes: Bytes,
}
impl LogBatch {
pub fn from_response_bytes(sequence_number: u64, response_bytes: impl Into<Bytes>) -> Self {
Self {
sequence_number,
response_bytes: response_bytes.into(),
}
}
pub fn from_entries(sequence_number: u64, kvs: Vec<(Bytes, Bytes)>) -> Self {
Self::from_response(StreamGetResponse {
sequence_number,
entries: kvs
.into_iter()
.map(|(key, value)| Entry {
key: key.to_vec(),
value,
..Default::default()
})
.collect(),
..Default::default()
})
}
pub fn from_response(response: StreamGetResponse) -> Self {
Self {
sequence_number: response.sequence_number,
response_bytes: Bytes::from(response.encode_to_vec()),
}
}
pub fn sequence_number(&self) -> u64 {
self.sequence_number
}
pub fn into_response_bytes(self) -> Bytes {
self.response_bytes
}
pub fn decode_response(&self) -> Result<StreamGetResponse, String> {
StreamGetResponse::decode_from_slice(&self.response_bytes)
.map_err(|err| format!("failed to decode sequence log value: {err}"))
}
}
pub trait Log: Sequence {
fn get_batch(
&self,
sequence_number: u64,
) -> impl Future<Output = Result<Option<LogBatch>, String>> + Send;
fn oldest_retained_batch(&self) -> impl Future<Output = Result<Option<u64>, String>> + Send;
}
pub trait StoreEngine: Ingest + Query + Prune + Log {}
impl<T: Ingest + Query + Prune + Log> StoreEngine for T {}