use super::hrpset::HrpSet;
use crate::address::errors::AddressBech32EncodeError;
use crate::network::NetworkDefinition;
use crate::types::EntityType;
use bech32::{self, ToBase32, Variant, WriteBase32};
use sbor::rust::prelude::*;
#[derive(Debug)]
pub struct AddressBech32Encoder {
pub hrp_set: HrpSet,
}
impl AddressBech32Encoder {
pub fn for_simulator() -> Self {
Self::new(&NetworkDefinition::simulator())
}
pub fn new(network: &NetworkDefinition) -> Self {
Self {
hrp_set: network.into(),
}
}
pub fn encode(&self, full_data: &[u8]) -> Result<String, AddressBech32EncodeError> {
let mut buf = String::new();
self.encode_to_fmt(&mut buf, full_data)?;
Ok(buf)
}
pub fn encode_to_fmt<F: fmt::Write>(
&self,
fmt: &mut F,
full_data: &[u8],
) -> Result<(), AddressBech32EncodeError> {
let entity_type = EntityType::from_repr(
*full_data
.first()
.ok_or(AddressBech32EncodeError::MissingEntityTypeByte)?,
)
.ok_or_else(|| AddressBech32EncodeError::InvalidEntityTypeId(full_data[0]))?;
let hrp = self.hrp_set.get_entity_hrp(&entity_type);
match bech32_encode_to_fmt(fmt, hrp, full_data.to_base32(), Variant::Bech32m) {
Ok(Ok(())) => Ok(()),
Ok(Err(format_error)) => Err(AddressBech32EncodeError::FormatError(format_error)),
Err(encoding_error) => Err(AddressBech32EncodeError::Bech32mEncodingError(
encoding_error,
)),
}
}
}
pub fn bech32_encode_to_fmt<F: fmt::Write, T: AsRef<[bech32::u5]>>(
fmt: &mut F,
hrp: &str,
data: T,
variant: Variant,
) -> Result<fmt::Result, bech32::Error> {
let hrp_lower = match bech32_check_hrp(hrp)? {
Bech32Case::Upper => Cow::Owned(hrp.to_lowercase()),
Bech32Case::Lower | Bech32Case::None => Cow::Borrowed(hrp),
};
match bech32::Bech32Writer::new(&hrp_lower, variant, fmt) {
Ok(mut writer) => {
Ok(writer.write(data.as_ref()).and_then(|_| {
writer.finalize()
}))
}
Err(e) => Ok(Err(e)),
}
}
fn bech32_check_hrp(hrp: &str) -> Result<Bech32Case, bech32::Error> {
if hrp.is_empty() || hrp.len() > 83 {
return Err(bech32::Error::InvalidLength);
}
let mut has_lower: bool = false;
let mut has_upper: bool = false;
for b in hrp.bytes() {
if !(33..=126).contains(&b) {
return Err(bech32::Error::InvalidChar(b as char));
}
if b.is_ascii_lowercase() {
has_lower = true;
} else if b.is_ascii_uppercase() {
has_upper = true;
};
if has_lower && has_upper {
return Err(bech32::Error::MixedCase);
}
}
Ok(match (has_upper, has_lower) {
(true, false) => Bech32Case::Upper,
(false, true) => Bech32Case::Lower,
(false, false) => Bech32Case::None,
(true, true) => unreachable!(),
})
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum Bech32Case {
Upper,
Lower,
None,
}