use crate::{
ConsumerGroupId, ConsumerMemberId, ENVELOPE_BYTES, Envelope, EnvelopeLimits, Offset, Opcode,
OperationId, Packet, PartitionIncarnation,
append::{Authority, Policy},
data::CodecError,
layout::Reader,
wire::{check, command, decode_authority, encode_authority, nonzero, reserve},
};
pub const MAX_MEMBERS: usize = 256;
pub const MAX_PARTITIONS: usize = 1024;
pub const MAX_NAME_BYTES: usize = 255;
pub const JOIN_FIXED_BYTES: usize = 112;
pub const OWNED_BYTES: usize = 32;
pub const JOINED_FIXED_BYTES: usize = 78;
pub const TARGET_BYTES: usize = 45;
pub const ASSIGN_BYTES: usize = 99;
pub const ASSIGNED_BYTES: usize = 122;
const SOURCE_TAG: u8 = 1;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum Start {
#[default]
Earliest,
Latest,
Offset(Offset),
}
impl Start {
pub const BYTES: usize = 9;
pub const fn to_wire(self) -> (u8, u64) {
match self {
Self::Earliest => (0, 0),
Self::Latest => (1, 0),
Self::Offset(offset) => (2, offset.get()),
}
}
pub const fn from_wire(tag: u8, value: u64) -> Option<Self> {
match (tag, value) {
(0, 0) => Some(Self::Earliest),
(1, 0) => Some(Self::Latest),
(2, value) => Some(Self::Offset(Offset::new(value))),
_ => None,
}
}
fn encode(self, output: &mut Vec<u8>) {
let (tag, value) = self.to_wire();
output.push(tag);
output.extend_from_slice(&value.to_be_bytes());
}
fn decode(cursor: &mut Reader<'_>) -> Result<Self, CodecError> {
let tag = cursor.u8()?;
let value = cursor.u64()?;
Self::from_wire(tag, value).ok_or(CodecError::Profile)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum Mode {
Heartbeat = 0,
Join = 1,
Leave = 2,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Owned {
pub partition: PartitionIncarnation,
pub plan_revision: u64,
pub epoch: u64,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct JoinRequest {
pub authority: Authority,
pub coordinator: PartitionIncarnation,
pub name: String,
pub member: ConsumerMemberId,
pub join: OperationId,
pub group: Option<ConsumerGroupId>,
pub mode: Mode,
pub initial: Start,
pub owned: Vec<Owned>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum Status {
Member = 0,
Joining = 1,
Unknown = 2,
Deleted = 3,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Target {
pub partition: PartitionIncarnation,
pub number: u32,
pub plan_revision: u64,
pub activatable: bool,
pub prior_member: Option<ConsumerMemberId>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct JoinReply {
pub authority: Authority,
pub coordinator: PartitionIncarnation,
pub group: ConsumerGroupId,
pub revision: u64,
pub status: Status,
pub targets: Vec<Target>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct AssignRequest {
pub authority: Authority,
pub partition: PartitionIncarnation,
pub group: ConsumerGroupId,
pub member: Option<ConsumerMemberId>,
pub plan_revision: u64,
pub initial: Start,
pub policy: Policy,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct AssignReply {
pub authority: Authority,
pub partition: PartitionIncarnation,
pub group: ConsumerGroupId,
pub member: Option<ConsumerMemberId>,
pub epoch: u64,
pub plan_revision: u64,
pub cursor: Offset,
pub retained_from: Offset,
pub end: Offset,
pub policy: Policy,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InitialPosition {
pub cursor: Offset,
pub retained_from: Offset,
pub end: Offset,
}
impl InitialPosition {
pub fn encode(self) -> Result<[u8; 24], CodecError> {
self.validate()?;
let mut bytes = [0; 24];
bytes[..8].copy_from_slice(&self.cursor.get().to_be_bytes());
bytes[8..16].copy_from_slice(&self.retained_from.get().to_be_bytes());
bytes[16..].copy_from_slice(&self.end.get().to_be_bytes());
Ok(bytes)
}
pub fn decode(bytes: &[u8]) -> Result<Self, CodecError> {
if bytes.len() != 24 {
return Err(CodecError::Length);
}
let mut cursor = Reader::new(bytes);
let detail = Self {
cursor: Offset::new(cursor.u64()?),
retained_from: Offset::new(cursor.u64()?),
end: Offset::new(cursor.u64()?),
};
detail.validate()?;
Ok(detail)
}
fn validate(self) -> Result<(), CodecError> {
if self.retained_from > self.end || (self.retained_from..=self.end).contains(&self.cursor) {
return Err(CodecError::Profile);
}
Ok(())
}
}
fn member_bytes(member: Option<ConsumerMemberId>) -> [u8; 16] {
member.map_or([0; 16], |member| *member.as_bytes())
}
fn decode_member(cursor: &mut Reader<'_>) -> Result<Option<ConsumerMemberId>, CodecError> {
let member = cursor.array::<16>()?;
Ok((member != [0; 16]).then(|| ConsumerMemberId::from_bytes(member)))
}
fn validate_scope(authority: Authority, partition: PartitionIncarnation) -> Result<(), CodecError> {
nonzero(authority.group_id.as_bytes())?;
nonzero(partition.as_bytes())?;
if authority.config_epoch == 0 {
return Err(CodecError::Identity);
}
Ok(())
}
mod admin;
mod assign;
mod join;
pub use admin::*;
pub use assign::*;
pub use join::*;
#[cfg(test)]
mod tests;