use core::iter::Iterator;
use core::marker::PhantomData;
use crate::primitives::checksum::HrpFe32Iter;
use crate::primitives::hrp::{self, Hrp};
use crate::primitives::iter::Checksummed;
use crate::{Checksum, Fe32};
#[derive(Clone, PartialEq, Eq)]
pub struct Encoder<'hrp, I, Ck>
where
I: Iterator<Item = Fe32>,
Ck: Checksum,
{
data: I,
hrp: &'hrp Hrp,
witness_version: Option<Fe32>,
marker: PhantomData<Ck>,
}
impl<'hrp, I, Ck> Encoder<'hrp, I, Ck>
where
I: Iterator<Item = Fe32>,
Ck: Checksum,
{
#[inline]
pub fn new(data: I, hrp: &'hrp Hrp) -> Self {
Self { data, hrp, witness_version: None, marker: PhantomData::<Ck> }
}
#[inline]
pub fn with_witness_version(mut self, witness_version: Fe32) -> Self {
self.witness_version = Some(witness_version);
self
}
#[inline]
pub fn chars(self) -> CharIter<'hrp, I, Ck> {
let witver_iter = WitnessVersionIter::new(self.witness_version, self.data);
CharIter::new(self.hrp, witver_iter)
}
#[inline]
pub fn bytes(self) -> ByteIter<'hrp, I, Ck> {
let char_iter = self.chars();
ByteIter::new(char_iter)
}
#[inline]
pub fn fes(self) -> Fe32Iter<'hrp, I, Ck> {
let witver_iter = WitnessVersionIter::new(self.witness_version, self.data);
Fe32Iter::new(self.hrp, witver_iter)
}
}
pub struct WitnessVersionIter<I>
where
I: Iterator<Item = Fe32>,
{
witness_version: Option<Fe32>,
iter: I,
}
impl<I> WitnessVersionIter<I>
where
I: Iterator<Item = Fe32>,
{
#[inline]
pub fn new(witness_version: Option<Fe32>, iter: I) -> Self { Self { witness_version, iter } }
}
impl<I> Iterator for WitnessVersionIter<I>
where
I: Iterator<Item = Fe32>,
{
type Item = Fe32;
#[inline]
fn next(&mut self) -> Option<Fe32> { self.witness_version.take().or_else(|| self.iter.next()) }
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let (min, max) = self.iter.size_hint();
match self.witness_version {
Some(_) => (min + 1, max.map(|max| max + 1)),
None => (min, max),
}
}
}
pub struct CharIter<'hrp, I, Ck>
where
I: Iterator<Item = Fe32>,
Ck: Checksum,
{
hrp_iter: Option<hrp::LowercaseCharIter<'hrp>>,
checksummed: Checksummed<WitnessVersionIter<I>, Ck>,
}
impl<'hrp, I, Ck> CharIter<'hrp, I, Ck>
where
I: Iterator<Item = Fe32>,
Ck: Checksum,
{
#[inline]
pub fn new(hrp: &'hrp Hrp, data: WitnessVersionIter<I>) -> Self {
let checksummed = Checksummed::new_hrp(*hrp, data);
Self { hrp_iter: Some(hrp.lowercase_char_iter()), checksummed }
}
}
impl<'a, I, Ck> Iterator for CharIter<'a, I, Ck>
where
I: Iterator<Item = Fe32>,
Ck: Checksum,
{
type Item = char;
#[inline]
fn next(&mut self) -> Option<char> {
if let Some(ref mut hrp_iter) = self.hrp_iter {
match hrp_iter.next() {
Some(c) => return Some(c),
None => {
self.hrp_iter = None;
return Some('1');
}
}
}
self.checksummed.next().map(|fe| fe.to_char())
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
match &self.hrp_iter {
None => self.checksummed.size_hint(),
Some(hrp_iter) => {
let (hrp_min, hrp_max) = hrp_iter.size_hint();
let (chk_min, chk_max) = self.checksummed.size_hint();
let min = hrp_min + 1 + chk_min;
let max = match (hrp_max, chk_max) {
(Some(hrp_max), Some(chk_max)) => Some(hrp_max + 1 + chk_max),
(_, _) => None,
};
(min, max)
}
}
}
}
pub struct ByteIter<'hrp, I, Ck>
where
I: Iterator<Item = Fe32>,
Ck: Checksum,
{
char_iter: CharIter<'hrp, I, Ck>,
}
impl<'hrp, I, Ck> ByteIter<'hrp, I, Ck>
where
I: Iterator<Item = Fe32>,
Ck: Checksum,
{
#[inline]
pub fn new(char_iter: CharIter<'hrp, I, Ck>) -> Self { Self { char_iter } }
}
impl<'a, I, Ck> Iterator for ByteIter<'a, I, Ck>
where
I: Iterator<Item = Fe32>,
Ck: Checksum,
{
type Item = u8;
#[inline]
fn next(&mut self) -> Option<u8> { self.char_iter.next().map(|c| c as u8) }
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) { self.char_iter.size_hint() }
}
pub struct Fe32Iter<'hrp, I, Ck>
where
I: Iterator<Item = Fe32>,
Ck: Checksum,
{
hrp_iter: Option<HrpFe32Iter<'hrp>>,
checksummed: Checksummed<WitnessVersionIter<I>, Ck>,
}
impl<'hrp, I, Ck> Fe32Iter<'hrp, I, Ck>
where
I: Iterator<Item = Fe32>,
Ck: Checksum,
{
#[inline]
pub fn new(hrp: &'hrp Hrp, data: WitnessVersionIter<I>) -> Self {
let hrp_iter = HrpFe32Iter::new(hrp);
let checksummed = Checksummed::new_hrp(*hrp, data);
Self { hrp_iter: Some(hrp_iter), checksummed }
}
}
impl<'hrp, I, Ck> Iterator for Fe32Iter<'hrp, I, Ck>
where
I: Iterator<Item = Fe32>,
Ck: Checksum,
{
type Item = Fe32;
#[inline]
fn next(&mut self) -> Option<Fe32> {
if let Some(ref mut hrp_iter) = &mut self.hrp_iter {
match hrp_iter.next() {
Some(fe) => return Some(fe),
None => self.hrp_iter = None,
}
}
self.checksummed.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let hrp = match &self.hrp_iter {
Some(hrp_iter) => hrp_iter.size_hint(),
None => (0, Some(0)),
};
let data = self.checksummed.size_hint();
let min = hrp.0 + data.0;
let max = hrp.1.zip(data.1).map(|(hrp, data)| hrp + data);
(min, max)
}
}
#[cfg(test)]
mod tests {
use crate::{Bech32, ByteIterExt, Fe32, Fe32IterExt, Hrp};
#[rustfmt::skip]
const DATA: [u8; 20] = [
0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4,
0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23,
0xf1, 0x43, 0x3b, 0xd6,
];
#[test]
fn hrpstring_iter() {
let iter = DATA.iter().copied().bytes_to_fes();
let hrp = Hrp::parse_unchecked("bc");
let iter = iter.with_checksum::<Bech32>(&hrp).with_witness_version(Fe32::Q).chars();
assert!(iter.eq("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4".chars()));
}
#[test]
#[cfg(feature = "alloc")]
fn hrpstring_iter_collect() {
let iter = DATA.iter().copied().bytes_to_fes();
let hrp = Hrp::parse_unchecked("bc");
let iter = iter.with_checksum::<Bech32>(&hrp).with_witness_version(Fe32::Q).chars();
let encoded = iter.collect::<String>();
assert_eq!(encoded, "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4");
}
#[test]
fn hrpstring_iter_size_hint() {
let char_len = "w508d6qejxtdg4y5r3zarvary0c5xw7k".len();
let iter = DATA.iter().copied().bytes_to_fes();
let hrp = Hrp::parse_unchecked("bc");
let iter = iter.with_checksum::<Bech32>(&hrp).with_witness_version(Fe32::Q).chars();
let checksummed_len = 2 + 1 + 1 + char_len + 6; assert_eq!(iter.size_hint().0, checksummed_len);
}
#[test]
#[cfg(feature = "alloc")]
fn hrpstring_iter_bytes() {
let hrp = Hrp::parse_unchecked("bc");
let fes = DATA.iter().copied().bytes_to_fes();
let iter = fes.with_checksum::<Bech32>(&hrp).with_witness_version(Fe32::Q);
let chars = iter.clone().chars();
let bytes = iter.bytes();
for (c, b) in chars.zip(bytes) {
assert_eq!(c as u8, b)
}
}
}