use crate::WireError;
use crate::codec::{WireDecode, WireEncode, read_u32_le};
use crate::responses::streams::get_stream::TopicHeader;
use bytes::{BufMut, BytesMut};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GetTopicsResponse {
pub topics: Vec<TopicHeader>,
}
impl WireEncode for GetTopicsResponse {
fn encoded_size(&self) -> usize {
4 + self
.topics
.iter()
.map(WireEncode::encoded_size)
.sum::<usize>()
}
fn encode(&self, buf: &mut BytesMut) {
#[allow(clippy::cast_possible_truncation)]
buf.put_u32_le(self.topics.len() as u32);
for topic in &self.topics {
topic.encode(buf);
}
}
}
const MIN_TOPIC_HEADER_SIZE: usize = TopicHeader::FIXED_SIZE + 1 + 4 + 4;
impl WireDecode for GetTopicsResponse {
fn decode(buf: &[u8]) -> Result<(Self, usize), WireError> {
let topics_count = read_u32_le(buf, 0)? as usize;
let mut pos = 4;
let mut topics = Vec::with_capacity(crate::codec::bounded_capacity(
topics_count,
buf.len().saturating_sub(pos),
MIN_TOPIC_HEADER_SIZE,
));
for _ in 0..topics_count {
let (topic, consumed) = TopicHeader::decode(&buf[pos..])?;
pos += consumed;
topics.push(topic);
}
Ok((Self { topics }, pos))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{WireName, WireOptions};
fn sample_topic(id: u32, name: &str) -> TopicHeader {
TopicHeader {
id,
created_at: 1_710_000_000_000,
partitions_count: 3,
message_expiry: 0,
compression_algorithm: 1,
max_topic_size: 0,
size_bytes: 1024,
messages_count: 100,
name: WireName::new(name).unwrap(),
options: WireOptions::empty(),
derived_options: WireOptions::empty(),
}
}
#[test]
fn roundtrip_empty() {
let resp = GetTopicsResponse { topics: vec![] };
let bytes = resp.to_bytes();
assert_eq!(bytes.len(), 4);
let (decoded, consumed) = GetTopicsResponse::decode(&bytes).unwrap();
assert_eq!(consumed, bytes.len());
assert_eq!(decoded, resp);
}
#[test]
fn roundtrip_multiple() {
let resp = GetTopicsResponse {
topics: vec![sample_topic(1, "events"), sample_topic(2, "logs")],
};
let bytes = resp.to_bytes();
let (decoded, consumed) = GetTopicsResponse::decode(&bytes).unwrap();
assert_eq!(consumed, bytes.len());
assert_eq!(decoded, resp);
}
#[test]
fn truncated_returns_error() {
let resp = GetTopicsResponse {
topics: vec![sample_topic(1, "t")],
};
let bytes = resp.to_bytes();
for i in 1..bytes.len() {
assert!(
GetTopicsResponse::decode(&bytes[..i]).is_err(),
"expected error for truncation at byte {i}"
);
}
}
}