use serde::{Deserialize, Serialize};
use std::ops::{Add, AddAssign};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(transparent)]
pub struct MaxBlockRange(u64);
impl MaxBlockRange {
pub const DEFAULT: Self = Self(2000);
pub const MODERATE: Self = Self(5000);
pub const GENEROUS: Self = Self(10000);
pub const CONSERVATIVE: Self = Self(1000);
pub const fn new(blocks: u64) -> Self {
Self(blocks)
}
pub const fn as_u64(&self) -> u64 {
self.0
}
pub fn chunks_needed(&self, start: u64, end: u64) -> usize {
if end < start {
return 0;
}
let total_blocks = end - start + 1;
total_blocks.div_ceil(self.0) as usize
}
pub fn chunk_range(&self, start: u64, end: u64) -> ChunkIterator {
ChunkIterator {
current: start,
end,
chunk_size: self.0,
}
}
}
impl From<u64> for MaxBlockRange {
fn from(value: u64) -> Self {
Self(value)
}
}
impl std::fmt::Display for MaxBlockRange {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} blocks", self.0)
}
}
#[derive(Debug, Clone)]
pub struct ChunkIterator {
current: u64,
end: u64,
chunk_size: u64,
}
impl Iterator for ChunkIterator {
type Item = (u64, u64);
fn next(&mut self) -> Option<Self::Item> {
if self.current > self.end {
return None;
}
let chunk_start = self.current;
let chunk_end = (self.current + self.chunk_size - 1).min(self.end);
self.current = chunk_end + 1;
Some((chunk_start, chunk_end))
}
fn size_hint(&self) -> (usize, Option<usize>) {
if self.current > self.end {
(0, Some(0))
} else {
let remaining_blocks = self.end - self.current + 1;
let chunks = remaining_blocks.div_ceil(self.chunk_size) as usize;
(chunks, Some(chunks))
}
}
}
impl ExactSizeIterator for ChunkIterator {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
#[serde(transparent)]
pub struct TransactionCount(usize);
impl TransactionCount {
pub const ZERO: Self = Self(0);
pub const fn new(count: usize) -> Self {
Self(count)
}
pub const fn as_usize(&self) -> usize {
self.0
}
pub fn increment(&mut self) {
self.0 = self.0.saturating_add(1);
}
pub fn is_zero(&self) -> bool {
self.0 == 0
}
}
impl From<usize> for TransactionCount {
fn from(value: usize) -> Self {
Self(value)
}
}
impl Add for TransactionCount {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0.saturating_add(rhs.0))
}
}
impl AddAssign for TransactionCount {
fn add_assign(&mut self, rhs: Self) {
self.0 = self.0.saturating_add(rhs.0);
}
}
impl std::fmt::Display for TransactionCount {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} transactions", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
#[serde(transparent)]
pub struct BlockCount(u64);
impl BlockCount {
pub const ZERO: Self = Self(0);
pub const fn new(count: u64) -> Self {
Self(count)
}
pub const fn as_u64(&self) -> u64 {
self.0
}
pub fn is_zero(&self) -> bool {
self.0 == 0
}
}
impl From<u64> for BlockCount {
fn from(value: u64) -> Self {
Self(value)
}
}
impl Add for BlockCount {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0.saturating_add(rhs.0))
}
}
impl AddAssign for BlockCount {
fn add_assign(&mut self, rhs: Self) {
self.0 = self.0.saturating_add(rhs.0);
}
}
impl std::fmt::Display for BlockCount {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.0 == 1 {
write!(f, "1 block")
} else {
write!(f, "{} blocks", self.0)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_max_block_range_creation() {
let range = MaxBlockRange::new(2000);
assert_eq!(range.as_u64(), 2000);
}
#[test]
fn test_max_block_range_constants() {
assert_eq!(MaxBlockRange::CONSERVATIVE.as_u64(), 1000);
assert_eq!(MaxBlockRange::DEFAULT.as_u64(), 2000);
assert_eq!(MaxBlockRange::MODERATE.as_u64(), 5000);
assert_eq!(MaxBlockRange::GENEROUS.as_u64(), 10000);
}
#[test]
fn test_chunks_needed() {
let range = MaxBlockRange::new(1000);
assert_eq!(range.chunks_needed(0, 999), 1);
assert_eq!(range.chunks_needed(0, 1000), 2);
assert_eq!(range.chunks_needed(0, 2500), 3);
assert_eq!(range.chunks_needed(100, 50), 0);
assert_eq!(range.chunks_needed(100, 100), 1);
}
#[test]
fn test_chunk_range_exact_multiple() {
let range = MaxBlockRange::new(1000);
let chunks: Vec<_> = range.chunk_range(0, 2999).collect();
assert_eq!(chunks.len(), 3);
assert_eq!(chunks[0], (0, 999));
assert_eq!(chunks[1], (1000, 1999));
assert_eq!(chunks[2], (2000, 2999));
}
#[test]
fn test_chunk_range_partial_last_chunk() {
let range = MaxBlockRange::new(1000);
let chunks: Vec<_> = range.chunk_range(0, 2500).collect();
assert_eq!(chunks.len(), 3);
assert_eq!(chunks[0], (0, 999));
assert_eq!(chunks[1], (1000, 1999));
assert_eq!(chunks[2], (2000, 2500));
}
#[test]
fn test_chunk_range_single_chunk() {
let range = MaxBlockRange::new(1000);
let chunks: Vec<_> = range.chunk_range(0, 500).collect();
assert_eq!(chunks.len(), 1);
assert_eq!(chunks[0], (0, 500));
}
#[test]
fn test_chunk_range_single_block() {
let range = MaxBlockRange::new(1000);
let chunks: Vec<_> = range.chunk_range(100, 100).collect();
assert_eq!(chunks.len(), 1);
assert_eq!(chunks[0], (100, 100));
}
#[test]
fn test_chunk_range_empty() {
let range = MaxBlockRange::new(1000);
let chunks: Vec<_> = range.chunk_range(100, 50).collect();
assert_eq!(chunks.len(), 0);
}
#[test]
fn test_chunk_range_non_zero_start() {
let range = MaxBlockRange::new(1000);
let chunks: Vec<_> = range.chunk_range(5000, 7500).collect();
assert_eq!(chunks.len(), 3);
assert_eq!(chunks[0], (5000, 5999));
assert_eq!(chunks[1], (6000, 6999));
assert_eq!(chunks[2], (7000, 7500));
}
#[test]
fn test_chunk_iterator_size_hint() {
let range = MaxBlockRange::new(1000);
let mut iter = range.chunk_range(0, 2500);
assert_eq!(iter.size_hint(), (3, Some(3)));
iter.next();
assert_eq!(iter.size_hint(), (2, Some(2)));
iter.next();
assert_eq!(iter.size_hint(), (1, Some(1)));
iter.next();
assert_eq!(iter.size_hint(), (0, Some(0)));
}
#[test]
fn test_display() {
let range = MaxBlockRange::new(2000);
assert_eq!(format!("{}", range), "2000 blocks");
}
#[test]
fn test_serialization() {
let range = MaxBlockRange::new(2000);
let json = serde_json::to_string(&range).unwrap();
let deserialized: MaxBlockRange = serde_json::from_str(&json).unwrap();
assert_eq!(range, deserialized);
}
#[test]
fn test_conversions() {
let u64_val = 2000u64;
let range: MaxBlockRange = u64_val.into();
let back: u64 = range.as_u64();
assert_eq!(u64_val, back);
}
#[test]
fn test_ordering() {
let small = MaxBlockRange::CONSERVATIVE;
let medium = MaxBlockRange::DEFAULT;
let large = MaxBlockRange::GENEROUS;
assert!(small < medium);
assert!(medium < large);
assert!(small < large);
}
#[test]
fn test_real_world_scenario() {
let daily_blocks = 7200 * 24; let range = MaxBlockRange::MODERATE;
let chunks: Vec<_> = range.chunk_range(1000000, 1000000 + daily_blocks).collect();
assert_eq!(chunks.len(), 35);
assert_eq!(chunks[0].0, 1000000);
assert_eq!(chunks[34].1, 1000000 + daily_blocks);
for i in 0..chunks.len() - 1 {
assert_eq!(chunks[i].1 + 1, chunks[i + 1].0);
}
}
#[test]
fn test_transaction_count_creation() {
let count = TransactionCount::new(5);
assert_eq!(count.as_usize(), 5);
}
#[test]
fn test_transaction_count_zero() {
assert!(TransactionCount::ZERO.is_zero());
assert_eq!(TransactionCount::ZERO.as_usize(), 0);
}
#[test]
fn test_transaction_count_addition() {
let a = TransactionCount::new(5);
let b = TransactionCount::new(3);
let sum = a + b;
assert_eq!(sum.as_usize(), 8);
}
#[test]
fn test_transaction_count_increment() {
let mut count = TransactionCount::new(5);
count.increment();
assert_eq!(count.as_usize(), 6);
}
#[test]
fn test_transaction_count_saturating_addition() {
let max_count = TransactionCount::new(usize::MAX);
let small_count = TransactionCount::new(1);
let result = max_count + small_count;
assert_eq!(result.as_usize(), usize::MAX);
}
#[test]
fn test_transaction_count_saturating_increment() {
let mut count = TransactionCount::new(usize::MAX);
count.increment();
assert_eq!(count.as_usize(), usize::MAX);
}
#[test]
fn test_transaction_count_display() {
let count = TransactionCount::new(42);
assert_eq!(format!("{}", count), "42 transactions");
}
#[test]
fn test_transaction_count_serialization() {
let count = TransactionCount::new(10);
let json = serde_json::to_string(&count).unwrap();
let deserialized: TransactionCount = serde_json::from_str(&json).unwrap();
assert_eq!(count, deserialized);
}
#[test]
fn test_transaction_count_conversions() {
let usize_val = 42usize;
let count: TransactionCount = usize_val.into();
let back: usize = count.as_usize();
assert_eq!(usize_val, back);
}
#[test]
fn test_transaction_count_ordering() {
let small = TransactionCount::new(5);
let medium = TransactionCount::new(10);
let large = TransactionCount::new(20);
assert!(small < medium);
assert!(medium < large);
assert!(small < large);
}
}