use super::append::Authority;
use super::{ENVELOPE_BYTES, Envelope, EnvelopeError, EnvelopeLimits, Opcode, Packet};
use crate::{
GroupId, NodeId,
layout::{Fixed, LayoutError, Reader},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AuthorityHint {
pub authority: Authority,
pub primary: NodeId,
}
impl AuthorityHint {
pub fn encode(self) -> Result<[u8; 48], NackError> {
self.validate()?;
let mut bytes = [0; 48];
bytes[..16].copy_from_slice(self.authority.group_id.as_bytes());
bytes[16..24].copy_from_slice(&self.authority.config_epoch.to_be_bytes());
bytes[24..32].copy_from_slice(&self.authority.view.to_be_bytes());
bytes[32..].copy_from_slice(self.primary.as_bytes());
Ok(bytes)
}
pub fn decode(bytes: &[u8]) -> Result<Self, NackError> {
let bytes: &[u8; 48] = bytes.try_into().map_err(|_| NackError::Length)?;
let hint = Self {
authority: Authority {
group_id: GroupId::from_bytes(bytes.array::<0, 16>()),
config_epoch: bytes.u64_at::<16>(),
view: bytes.u64_at::<24>(),
},
primary: NodeId::from_bytes(bytes.array::<32, 16>()),
};
hint.validate()?;
Ok(hint)
}
fn validate(self) -> Result<(), NackError> {
if self.authority.group_id.as_bytes() == &[0; 16]
|| self.authority.config_epoch == 0
|| self.primary.as_bytes() == &[0; 16]
{
return Err(NackError::Authority);
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum RetryClass {
Permanent = 1,
AfterBackoff = 2,
AfterReconnect = 3,
AfterAuthorityRefresh = 4,
UnknownOutcome = 5,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Nack<'a> {
pub code: u16,
pub retry: RetryClass,
pub detail: &'a [u8],
pub diagnostic: &'a str,
}
pub fn encode(
envelope: Envelope,
nack: Nack<'_>,
metadata: &mut Vec<u8>,
limits: EnvelopeLimits,
) -> Result<[u8; ENVELOPE_BYTES], NackError> {
command(envelope)?;
let size = 11_usize
.checked_add(nack.detail.len())
.and_then(|n| n.checked_add(nack.diagnostic.len()))
.ok_or(NackError::Length)?;
let header = envelope.encode_header(size, 0, limits)?;
let detail = u32::try_from(nack.detail.len()).map_err(|_| NackError::Length)?;
let diagnostic = u32::try_from(nack.diagnostic.len()).map_err(|_| NackError::Length)?;
if metadata.capacity() < size {
return Err(NackError::Capacity);
}
metadata.clear();
metadata.extend_from_slice(&nack.code.to_be_bytes());
metadata.push(nack.retry as u8);
metadata.extend_from_slice(&detail.to_be_bytes());
metadata.extend_from_slice(nack.detail);
metadata.extend_from_slice(&diagnostic.to_be_bytes());
metadata.extend_from_slice(nack.diagnostic.as_bytes());
Ok(header)
}
pub fn decode(packet: Packet<'_>, limits: EnvelopeLimits) -> Result<Nack<'_>, NackError> {
command(packet.envelope)?;
packet
.envelope
.validate_frames(packet.metadata.len(), packet.payload.len(), limits)?;
if !packet.payload.is_empty() {
return Err(NackError::Command);
}
let mut reader = Reader::new(packet.metadata);
let code = reader.u16()?;
let retry = match reader.u8()? {
1 => RetryClass::Permanent,
2 => RetryClass::AfterBackoff,
3 => RetryClass::AfterReconnect,
4 => RetryClass::AfterAuthorityRefresh,
5 => RetryClass::UnknownOutcome,
_ => return Err(NackError::Command),
};
let size = reader.u32()? as usize;
let detail = reader.take(size)?;
let size = reader.u32()? as usize;
let diagnostic = std::str::from_utf8(reader.take(size)?).map_err(|_| NackError::Length)?;
reader.finish()?;
Ok(Nack {
code,
retry,
detail,
diagnostic,
})
}
fn command(envelope: Envelope) -> Result<(), NackError> {
if envelope.opcode != Opcode::Nack || !envelope.response || envelope.request_id.is_none() {
return Err(NackError::Command);
}
Ok(())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum NackError {
#[error("invalid NACK authority hint")]
Authority,
#[error(transparent)]
Envelope(#[from] EnvelopeError),
#[error("invalid NACK command")]
Command,
#[error("invalid NACK field length")]
Length,
#[error("NACK encode buffer capacity exhausted")]
Capacity,
}
impl From<LayoutError> for NackError {
fn from(_: LayoutError) -> Self {
Self::Length
}
}