use std::{fmt::Display, ptr};
use super::{Id, prefix::IdPrefix};
#[derive(Debug, Clone, Copy)]
pub struct CombinedId {
pub(crate) primary_id: Id,
pub(crate) secondary_id: Id,
}
impl CombinedId {
#[must_use]
#[allow(clippy::missing_panics_doc)]
pub fn to_slice(&self) -> [u8; 64] {
let mut value = [0; 64];
assert_eq!(self.primary_id.as_slice().len(), 32);
assert_eq!(self.secondary_id.as_slice().len(), 32);
unsafe {
ptr::copy_nonoverlapping(self.primary_id.as_slice().as_ptr(), value.as_mut_ptr(), 32);
ptr::copy_nonoverlapping(
self.secondary_id.as_slice().as_ptr(),
value.as_mut_ptr(),
32,
);
}
value
}
}
#[derive(Debug, Clone, Copy)]
pub struct CombinedIdPrefix {
primary_id: IdPrefix,
secondary_id: IdPrefix,
}
impl CombinedId {
#[must_use]
pub fn shorten(self) -> CombinedIdPrefix {
CombinedIdPrefix {
primary_id: self.primary_id.shorten(),
secondary_id: self.secondary_id.shorten(),
}
}
}
impl Display for CombinedIdPrefix {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut output = String::new();
let mut primary = self.primary_id.to_string();
let mut secondary = self.secondary_id.to_string();
for index in 0..(IdPrefix::REQUIRED_LENGTH * 2) {
match index {
i if i == 1 || i == 3 || i == 5 || i == 9 || (i >= 10 && i % 5 == 4) => {
output.push(
secondary
.pop()
.expect("This should always be valid as we max out at REQUIRED_PREFIX"),
);
}
i if i == 0
|| i == 2
|| i == 4
|| i == 6
|| i == 7
|| i == 8
|| (i >= 10 && i % 5 != 4) =>
{
output.push(
primary
.pop()
.expect("This should always be valid as we max out at REQUIRED_PREFIX"),
);
}
_ => unreachable!("All possible indices should be covered above."),
}
}
f.write_str(output.as_str())?;
Ok(())
}
}
#[allow(missing_docs)]
pub mod decode {
use std::str::FromStr;
use super::CombinedIdPrefix;
use crate::replica::entity::id::prefix::{self, IdPrefix};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(
"Your combined id {combined_id} was too long: {len} of expected 64",
len = combined_id.len()
)]
TooLong { combined_id: String },
#[error("The separated id prefixes from the combined id prefix could not be parsed: {0}")]
InvalidPrefix(#[from] prefix::decode::Error),
}
impl FromStr for CombinedIdPrefix {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() > 64 {
return Err(Error::TooLong {
combined_id: s.to_owned(),
});
}
let mut primary: [u8; 50] = [0; 50];
let mut primary_index = 0;
let mut secondary: [u8; 14] = [0; 14];
let mut secondary_index = 0;
for (index, ch) in s.chars().enumerate() {
match index {
i if i == 1 || i == 3 || i == 5 || i == 9 || (i >= 10 && i % 5 == 4) => {
secondary[secondary_index] = ch as u8;
secondary_index += 1;
}
i if i == 0
|| i == 2
|| i == 4
|| i == 6
|| i == 7
|| i == 8
|| (i >= 10 && i % 5 != 4) =>
{
primary[primary_index] = ch as u8;
primary_index += 1;
}
_ => unreachable!("All possible indices should be covered above."),
}
}
Ok(Self {
primary_id: IdPrefix::from_hex_bytes(&primary)?,
secondary_id: IdPrefix::from_hex_bytes(&secondary)?,
})
}
}
impl TryFrom<&str> for CombinedIdPrefix {
type Error = Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
<Self as FromStr>::from_str(value)
}
}
}