use std::{marker::PhantomData, sync::Arc};
use zebra_chain::block::{self, Block, Height};
use crate::{
service::{
finalized_state::ZebraDb,
non_finalized_state::{Chain, NonFinalizedState},
read,
},
HashOrHeight,
};
#[derive(Clone, Debug)]
pub(crate) struct Iter<Item: ChainItem> {
pub(super) chain: Option<Arc<Chain>>,
pub(super) db: ZebraDb,
pub(super) height: Option<Height>,
iterable: PhantomData<Item::Type>,
}
impl<Item> Iter<Item>
where
Item: ChainItem,
{
fn yield_by_height(&mut self) -> Option<Item::Type> {
let current_height = self.height?;
let item = Item::read(self.chain.as_ref(), &self.db, current_height);
self.height = current_height.previous().ok();
if let Some(chain) = self.chain.as_ref() {
if let Some(height) = self.height {
if !chain.contains_block_height(height) {
std::mem::drop(self.chain.take());
}
} else {
std::mem::drop(self.chain.take());
}
}
item
}
}
impl<Item> Iterator for Iter<Item>
where
Item: ChainItem,
{
type Item = Item::Type;
fn next(&mut self) -> Option<Self::Item> {
self.yield_by_height()
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
}
impl<Item> ExactSizeIterator for Iter<Item>
where
Item: ChainItem,
{
fn len(&self) -> usize {
self.height.map_or(0, |height| height.as_usize() + 1)
}
}
impl<Item> std::iter::FusedIterator for Iter<Item> where Item: ChainItem {}
pub(crate) trait ChainItem {
type Type;
fn read(chain: Option<&Arc<Chain>>, db: &ZebraDb, height: Height) -> Option<Self::Type>;
}
impl ChainItem for Block {
type Type = Arc<Block>;
fn read(chain: Option<&Arc<Chain>>, db: &ZebraDb, height: Height) -> Option<Self::Type> {
read::block(chain, db, height.into())
}
}
impl ChainItem for block::Header {
type Type = Arc<block::Header>;
fn read(chain: Option<&Arc<Chain>>, db: &ZebraDb, height: Height) -> Option<Self::Type> {
read::block_header(chain, db, height.into())
}
}
pub(crate) fn any_ancestor_blocks(
non_finalized_state: &NonFinalizedState,
db: &ZebraDb,
hash: block::Hash,
) -> Iter<Block> {
any_chain_ancestor_iter(non_finalized_state, db, hash)
}
pub(crate) fn any_chain_ancestor_iter<Item>(
non_finalized_state: &NonFinalizedState,
db: &ZebraDb,
hash: block::Hash,
) -> Iter<Item>
where
Item: ChainItem,
{
let chain = non_finalized_state.find_chain(|chain| chain.contains_block_hash(hash));
let height = read::height_by_hash(chain.as_ref(), db, hash);
Iter {
chain,
db: db.clone(),
height,
iterable: PhantomData,
}
}
#[allow(dead_code)]
pub(crate) fn known_chain_ancestor_iter<Item>(
chain: Option<Arc<Chain>>,
db: &ZebraDb,
hash_or_height: HashOrHeight,
) -> Iter<Item>
where
Item: ChainItem,
{
let height =
hash_or_height.height_or_else(|hash| read::height_by_hash(chain.as_ref(), db, hash));
Iter {
chain,
db: db.clone(),
height,
iterable: PhantomData,
}
}