use std::fmt;
use kacrab_protocol::KafkaUuid;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProducerPartitionInfo {
pub topic: String,
pub topic_id: KafkaUuid,
pub partition: i32,
pub leader_id: i32,
pub leader_epoch: i32,
pub replica_nodes: Vec<i32>,
pub isr_nodes: Vec<i32>,
pub offline_replicas: Vec<i32>,
}
pub type PartitionInfo = ProducerPartitionInfo;
impl ProducerPartitionInfo {
#[must_use]
#[expect(
clippy::too_many_arguments,
reason = "Constructor mirrors Kafka's PartitionInfo(topic, partition, leader, replicas, \
isr, offlineReplicas)."
)]
pub fn new(
topic: impl Into<String>,
partition: i32,
leader_id: i32,
replica_nodes: impl IntoIterator<Item = i32>,
in_sync_replicas: impl IntoIterator<Item = i32>,
offline_replicas: impl IntoIterator<Item = i32>,
) -> Self {
Self {
topic: topic.into(),
topic_id: KafkaUuid::ZERO,
partition,
leader_id,
leader_epoch: -1,
replica_nodes: replica_nodes.into_iter().collect(),
isr_nodes: in_sync_replicas.into_iter().collect(),
offline_replicas: offline_replicas.into_iter().collect(),
}
}
#[must_use]
pub const fn with_topic_id(mut self, topic_id: KafkaUuid) -> Self {
self.topic_id = topic_id;
self
}
#[must_use]
pub const fn with_leader_epoch(mut self, leader_epoch: i32) -> Self {
self.leader_epoch = leader_epoch;
self
}
#[must_use]
pub fn topic(&self) -> &str {
&self.topic
}
#[must_use]
pub const fn topic_id(&self) -> KafkaUuid {
self.topic_id
}
#[must_use]
pub const fn partition(&self) -> i32 {
self.partition
}
#[must_use]
pub const fn leader(&self) -> Option<i32> {
if self.leader_id < 0 {
None
} else {
Some(self.leader_id)
}
}
#[must_use]
pub const fn leader_id(&self) -> i32 {
self.leader_id
}
#[must_use]
pub const fn leader_epoch(&self) -> i32 {
self.leader_epoch
}
#[must_use]
pub fn replicas(&self) -> &[i32] {
&self.replica_nodes
}
#[must_use]
pub fn in_sync_replicas(&self) -> &[i32] {
&self.isr_nodes
}
#[must_use]
pub fn offline_replicas(&self) -> &[i32] {
&self.offline_replicas
}
}
impl fmt::Display for ProducerPartitionInfo {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"Partition(topic = {}, partition = {}, leader = {}, replicas = {}, isr = {}, \
offlineReplicas = {})",
self.topic,
self.partition,
self.leader()
.map_or_else(|| "none".to_owned(), |leader| leader.to_string()),
NodeIdList(&self.replica_nodes),
NodeIdList(&self.isr_nodes),
NodeIdList(&self.offline_replicas)
)
}
}
struct NodeIdList<'a>(&'a [i32]);
impl fmt::Display for NodeIdList<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("[")?;
for (index, node_id) in self.0.iter().enumerate() {
if index > 0 {
formatter.write_str(",")?;
}
write!(formatter, "{node_id}")?;
}
formatter.write_str("]")
}
}
pub use crate::common::{ConsumerGroupMetadata, OffsetAndMetadata, TopicPartition};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ProducerMetricSubscription {
pub name: String,
}
impl ProducerMetricSubscription {
#[must_use]
pub fn new(name: impl Into<String>) -> Self {
Self { name: name.into() }
}
}
#[cfg(test)]
mod tests {
use kacrab_protocol::KafkaUuid;
use super::PartitionInfo;
#[test]
fn partition_info_accessors_and_display_match_java_shape() {
let info = PartitionInfo::new("orders", 2, 7, [7, 8], [7], [8])
.with_topic_id(KafkaUuid::from_parts(1, 2))
.with_leader_epoch(42);
assert_eq!(info.topic(), "orders");
assert_eq!(info.topic_id(), KafkaUuid::from_parts(1, 2));
assert_eq!(info.partition(), 2);
assert_eq!(info.leader(), Some(7));
assert_eq!(info.leader_id(), 7);
assert_eq!(info.leader_epoch(), 42);
assert_eq!(info.replicas(), &[7, 8]);
assert_eq!(info.in_sync_replicas(), &[7]);
assert_eq!(info.offline_replicas(), &[8]);
assert_eq!(
info.to_string(),
"Partition(topic = orders, partition = 2, leader = 7, replicas = [7,8], isr = [7], \
offlineReplicas = [8])"
);
assert_eq!(
PartitionInfo::new("orders", 0, -1, [7], [], []).to_string(),
"Partition(topic = orders, partition = 0, leader = none, replicas = [7], isr = [], \
offlineReplicas = [])"
);
}
}