extern crate alloc;
use alloc::vec::Vec;
use thiserror::Error;
use super::super::gate::{EpochAddress, InvalidEpochAddress};
use super::shared::{WIRE_ADDRESS_LEN, encode_address_into, read_address_parts, read_u64};
const ADOPTION_REPORT_WIRE_V1: u8 = 0x01;
const ADOPTION_REPORT_WIRE_LEN: usize = 1 + WIRE_ADDRESS_LEN + 8;
#[non_exhaustive]
#[derive(Error, Debug, Clone, Copy, PartialEq, Eq)]
pub enum AdoptionReportDecodeError {
#[error("unknown adoption-report wire version: {0:#04x}")]
UnknownVersion(u8),
#[error("unexpected adoption-report frame length: expected {expected}, found {found}")]
UnexpectedLength {
expected: usize,
found: usize,
},
#[error("adoption-report address: {0}")]
Address(#[from] InvalidEpochAddress),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct AdoptionReportRecord {
epoch: EpochAddress,
own_counter: u64,
}
impl AdoptionReportRecord {
#[must_use]
pub const fn new(epoch: EpochAddress, own_counter: u64) -> Self {
Self { epoch, own_counter }
}
#[must_use]
pub const fn epoch(&self) -> EpochAddress {
self.epoch
}
#[must_use]
pub const fn own_counter(&self) -> u64 {
self.own_counter
}
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(ADOPTION_REPORT_WIRE_LEN);
out.push(ADOPTION_REPORT_WIRE_V1);
encode_address_into(&mut out, self.epoch);
out.extend_from_slice(&self.own_counter.to_be_bytes());
out
}
#[must_use]
pub const fn encoded_len(&self) -> usize {
ADOPTION_REPORT_WIRE_LEN
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, AdoptionReportDecodeError> {
if bytes.len() != ADOPTION_REPORT_WIRE_LEN {
return Err(AdoptionReportDecodeError::UnexpectedLength {
expected: ADOPTION_REPORT_WIRE_LEN,
found: bytes.len(),
});
}
let version = bytes[0];
if version != ADOPTION_REPORT_WIRE_V1 {
return Err(AdoptionReportDecodeError::UnknownVersion(version));
}
let short = AdoptionReportDecodeError::UnexpectedLength {
expected: ADOPTION_REPORT_WIRE_LEN,
found: bytes.len(),
};
let ((generation, dot), rest) = read_address_parts(&bytes[1..]).ok_or(short)?;
let epoch = EpochAddress::try_from_parts(generation, dot)?;
let (own_counter, _) = read_u64(rest).ok_or(short)?;
Ok(Self { epoch, own_counter })
}
}