use crate::{
core::{
cache::{CacheRead, CacheWrite, OwnedRef, Structured},
primitive::commitlog,
storage::Error as StorageError,
},
Addr, Error,
};
pub struct LogContainer<'a, T> {
pub addr: &'a Addr,
pub node: T,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[derive(Debug, Clone)]
pub struct LogNode<T> {
pub inner: T,
pub prev: Option<Addr>,
}
#[derive(Debug)]
pub enum LogInnerType {
Commit(commitlog::CommitNode),
}
impl From<commitlog::CommitNode> for LogInnerType {
fn from(n: commitlog::CommitNode) -> LogInnerType {
LogInnerType::Commit(n)
}
}
impl<T> From<LogNode<T>> for Structured
where
T: Into<LogInnerType>,
{
fn from(log_node: LogNode<T>) -> Self {
let LogNode { inner, prev } = log_node;
let LogInnerType::Commit(inner) = inner.into();
Structured::CommitLogNode(LogNode { inner, prev })
}
}
pub trait LogNodeFrom<T>: Sized {
fn log_node_from(t: T) -> Option<LogNode<Self>>;
}
impl LogNodeFrom<Structured> for commitlog::CommitNode {
fn log_node_from(t: Structured) -> Option<LogNode<Self>> {
if let Structured::CommitLogNode(node) = t {
Some(node)
} else {
None
}
}
}
pub struct AppendLog<'s, C> {
cache: &'s C,
addr: Option<Addr>,
}
impl<'s, C> AppendLog<'s, C> {
pub fn new(cache: &'s C, addr: Option<Addr>) -> Self {
Self { cache, addr }
}
}
impl<'s, C> AppendLog<'s, C>
where
C: CacheRead + CacheWrite,
{
pub async fn append<T>(&mut self, inner: T) -> Result<Addr, Error>
where
T: Into<LogInnerType> + Send,
{
let node = LogNode {
inner,
prev: self.addr.clone(),
};
let addr = self.cache.write_structured(node).await?;
let _ = self.addr.replace(addr.clone());
Ok(addr)
}
}
impl<'s, C> AppendLog<'s, C>
where
C: CacheRead,
{
pub async fn first_container<T>(&self) -> Result<Option<LogContainer<'_, LogNode<T>>>, Error>
where
T: LogNodeFrom<Structured>,
{
let addr = match self.addr.as_ref() {
Some(addr) => addr,
None => return Ok(None),
};
let owned_ref = self.cache.read_structured(addr).await?;
let node = T::log_node_from(owned_ref.into_owned_structured())
.ok_or_else(|| StorageError::Unhandled {
message: "misaligned cache types".to_owned(),
})?;
Ok(Some(LogContainer { addr, node }))
}
pub async fn first<T>(&self) -> Result<Option<LogNode<T>>, Error>
where
T: LogNodeFrom<Structured>,
{
let container = self.first_container().await?;
Ok(container.map(|LogContainer { node, .. }| node))
}
}