use std::fmt::{self, Debug, Display};
use std::ops::RangeBounds;
use async_trait::async_trait;
use celestia_types::ExtendedHeader;
use celestia_types::hash::Hash;
use cid::Cid;
use libp2p::identity::Keypair;
use crate::store::{BlockRanges, Result, SamplingMetadata, Store, VerifiedExtendedHeaders};
pub enum EitherStore<L, R>
where
L: Store,
R: Store,
{
Left(L),
Right(R),
}
impl<L, R> EitherStore<L, R>
where
L: Store,
R: Store,
{
pub fn is_left(&self) -> bool {
match self {
EitherStore::Left(_) => true,
EitherStore::Right(_) => false,
}
}
pub fn is_right(&self) -> bool {
match self {
EitherStore::Left(_) => false,
EitherStore::Right(_) => true,
}
}
pub fn left(&self) -> Option<&L> {
match self {
EitherStore::Left(store) => Some(store),
EitherStore::Right(_) => None,
}
}
pub fn right(&self) -> Option<&R> {
match self {
EitherStore::Left(_) => None,
EitherStore::Right(store) => Some(store),
}
}
pub fn left_mut(&mut self) -> Option<&mut L> {
match self {
EitherStore::Left(store) => Some(store),
EitherStore::Right(_) => None,
}
}
pub fn right_mut(&mut self) -> Option<&mut R> {
match self {
EitherStore::Left(_) => None,
EitherStore::Right(store) => Some(store),
}
}
pub fn into_left(self) -> Option<L> {
match self {
EitherStore::Left(store) => Some(store),
EitherStore::Right(_) => None,
}
}
pub fn into_right(self) -> Option<R> {
match self {
EitherStore::Left(_) => None,
EitherStore::Right(store) => Some(store),
}
}
}
impl<L, R> Clone for EitherStore<L, R>
where
L: Store + Clone,
R: Store + Clone,
{
fn clone(&self) -> Self {
match self {
EitherStore::Left(store) => EitherStore::Left(store.clone()),
EitherStore::Right(store) => EitherStore::Right(store.clone()),
}
}
fn clone_from(&mut self, source: &Self) {
match source {
EitherStore::Left(source_store) => match self {
EitherStore::Left(self_store) => self_store.clone_from(source_store),
EitherStore::Right(_) => *self = EitherStore::Left(source_store.clone()),
},
EitherStore::Right(source_store) => match self {
EitherStore::Left(_) => *self = EitherStore::Right(source_store.clone()),
EitherStore::Right(self_store) => self_store.clone_from(source_store),
},
};
}
}
impl<L, R> Debug for EitherStore<L, R>
where
L: Store + Debug,
R: Store + Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EitherStore::Left(store) => Debug::fmt(store, f),
EitherStore::Right(store) => Debug::fmt(store, f),
}
}
}
impl<L, R> Display for EitherStore<L, R>
where
L: Store + Display,
R: Store + Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EitherStore::Left(store) => Display::fmt(store, f),
EitherStore::Right(store) => Display::fmt(store, f),
}
}
}
macro_rules! call {
($self:ident, $method:ident($($param:expr),*)) => {
match $self {
EitherStore::Left(store) => store.$method($($param),*).await,
EitherStore::Right(store) => store.$method($($param),*).await,
}
};
}
#[async_trait]
impl<L, R> Store for EitherStore<L, R>
where
L: Store,
R: Store,
{
async fn get_head(&self) -> Result<ExtendedHeader> {
call!(self, get_head())
}
async fn get_by_hash(&self, hash: &Hash) -> Result<ExtendedHeader> {
call!(self, get_by_hash(hash))
}
async fn get_by_height(&self, height: u64) -> Result<ExtendedHeader> {
call!(self, get_by_height(height))
}
async fn wait_new_head(&self) -> u64 {
call!(self, wait_new_head())
}
async fn wait_height(&self, height: u64) -> Result<()> {
call!(self, wait_height(height))
}
async fn get_range<RB>(&self, range: RB) -> Result<Vec<ExtendedHeader>>
where
RB: RangeBounds<u64> + Send,
{
call!(self, get_range(range))
}
async fn head_height(&self) -> Result<u64> {
call!(self, head_height())
}
async fn has(&self, hash: &Hash) -> bool {
call!(self, has(hash))
}
async fn has_at(&self, height: u64) -> bool {
call!(self, has_at(height))
}
async fn update_sampling_metadata(&self, height: u64, cids: Vec<Cid>) -> Result<()> {
call!(self, update_sampling_metadata(height, cids))
}
async fn get_sampling_metadata(&self, height: u64) -> Result<Option<SamplingMetadata>> {
call!(self, get_sampling_metadata(height))
}
async fn mark_as_sampled(&self, height: u64) -> Result<()> {
call!(self, mark_as_sampled(height))
}
async fn insert<H>(&self, headers: H) -> Result<()>
where
H: TryInto<VerifiedExtendedHeaders> + Send,
<H as TryInto<VerifiedExtendedHeaders>>::Error: Display,
{
call!(self, insert(headers))
}
async fn get_stored_header_ranges(&self) -> Result<BlockRanges> {
call!(self, get_stored_header_ranges())
}
async fn get_sampled_ranges(&self) -> Result<BlockRanges> {
call!(self, get_sampled_ranges())
}
async fn get_pruned_ranges(&self) -> Result<BlockRanges> {
call!(self, get_pruned_ranges())
}
async fn remove_height(&self, height: u64) -> Result<()> {
call!(self, remove_height(height))
}
async fn get_identity(&self) -> Result<Keypair> {
call!(self, get_identity())
}
async fn close(self) -> Result<()> {
call!(self, close())
}
}