1use crate::codec::{Decoder, Encoder};
2use crate::error::Result;
3use crate::header::RequestHeader;
4
5pub const API_KEY: i16 = 3;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct MetadataRequestV1 {
9 pub correlation_id: i32,
10 pub client_id: Option<String>,
11 pub topics: Option<Vec<String>>,
12}
13
14impl MetadataRequestV1 {
15 pub fn encode(&self) -> Result<Vec<u8>> {
16 let mut encoder = Encoder::new();
17 RequestHeader {
18 api_key: API_KEY,
19 api_version: 1,
20 correlation_id: self.correlation_id,
21 client_id: self.client_id.clone(),
22 }
23 .encode_v1(&mut encoder)?;
24 encoder.write_array(self.topics.as_deref(), |encoder, topic| {
25 encoder.write_string(topic)
26 })?;
27 Ok(encoder.into_bytes())
28 }
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct BrokerMetadata {
33 pub node_id: i32,
34 pub host: String,
35 pub port: i32,
36 pub rack: Option<String>,
37}
38
39impl BrokerMetadata {
40 fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
41 Ok(Self {
42 node_id: decoder.read_i32()?,
43 host: decoder.read_string()?,
44 port: decoder.read_i32()?,
45 rack: decoder.read_nullable_string()?,
46 })
47 }
48}
49
50#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct PartitionMetadata {
52 pub error_code: i16,
53 pub partition_index: i32,
54 pub leader_id: i32,
55 pub replica_nodes: Vec<i32>,
56 pub isr_nodes: Vec<i32>,
57}
58
59impl PartitionMetadata {
60 fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
61 Ok(Self {
62 error_code: decoder.read_i16()?,
63 partition_index: decoder.read_i32()?,
64 leader_id: decoder.read_i32()?,
65 replica_nodes: decoder
66 .read_array("replica nodes", |decoder| decoder.read_i32())?
67 .unwrap_or_default(),
68 isr_nodes: decoder
69 .read_array("isr nodes", |decoder| decoder.read_i32())?
70 .unwrap_or_default(),
71 })
72 }
73}
74
75#[derive(Debug, Clone, PartialEq, Eq)]
76pub struct TopicMetadata {
77 pub error_code: i16,
78 pub name: String,
79 pub is_internal: bool,
80 pub partitions: Vec<PartitionMetadata>,
81}
82
83impl TopicMetadata {
84 fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
85 Ok(Self {
86 error_code: decoder.read_i16()?,
87 name: decoder.read_string()?,
88 is_internal: decoder.read_bool()?,
89 partitions: decoder
90 .read_array("partitions", PartitionMetadata::decode)?
91 .unwrap_or_default(),
92 })
93 }
94}
95
96#[derive(Debug, Clone, PartialEq, Eq)]
97pub struct MetadataResponseV1 {
98 pub brokers: Vec<BrokerMetadata>,
99 pub controller_id: i32,
100 pub topics: Vec<TopicMetadata>,
101}
102
103impl MetadataResponseV1 {
104 pub fn decode_body(decoder: &mut Decoder<'_>) -> Result<Self> {
105 Ok(Self {
106 brokers: decoder
107 .read_array("brokers", BrokerMetadata::decode)?
108 .unwrap_or_default(),
109 controller_id: decoder.read_i32()?,
110 topics: decoder
111 .read_array("topics", TopicMetadata::decode)?
112 .unwrap_or_default(),
113 })
114 }
115}
116
117#[derive(Debug, Clone, PartialEq, Eq)]
119pub struct MetadataRequestTopicV12 {
120 pub topic_id: [u8; 16],
121 pub name: Option<String>,
122}
123
124impl MetadataRequestTopicV12 {
125 fn encode(&self, encoder: &mut Encoder) -> Result<()> {
126 encoder.write_uuid(&self.topic_id);
127 encoder.write_compact_nullable_string(self.name.as_deref())?;
128 encoder.write_empty_tagged_fields();
129 Ok(())
130 }
131}
132
133#[derive(Debug, Clone, PartialEq, Eq)]
135pub struct MetadataRequestV12 {
136 pub correlation_id: i32,
137 pub client_id: Option<String>,
138 pub topics: Option<Vec<MetadataRequestTopicV12>>,
139 pub allow_auto_topic_creation: bool,
140 pub include_topic_authorized_operations: bool,
141}
142
143impl MetadataRequestV12 {
144 pub fn encode(&self) -> Result<Vec<u8>> {
145 let mut encoder = Encoder::new();
146 RequestHeader {
147 api_key: API_KEY,
148 api_version: 12,
149 correlation_id: self.correlation_id,
150 client_id: self.client_id.clone(),
151 }
152 .encode_v2(&mut encoder)?;
153 encoder.write_compact_array(self.topics.as_deref(), |encoder, topic| {
154 topic.encode(encoder)
155 })?;
156 encoder.write_bool(self.allow_auto_topic_creation);
157 encoder.write_bool(self.include_topic_authorized_operations);
158 encoder.write_empty_tagged_fields();
159 Ok(encoder.into_bytes())
160 }
161}
162
163#[derive(Debug, Clone, PartialEq, Eq)]
165pub struct MetadataBrokerV12 {
166 pub node_id: i32,
167 pub host: String,
168 pub port: i32,
169 pub rack: Option<String>,
170}
171
172impl MetadataBrokerV12 {
173 fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
174 let broker = Self {
175 node_id: decoder.read_i32()?,
176 host: decoder.read_compact_string()?,
177 port: decoder.read_i32()?,
178 rack: decoder.read_compact_nullable_string()?,
179 };
180 decoder.read_tagged_fields()?;
181 Ok(broker)
182 }
183}
184
185#[derive(Debug, Clone, PartialEq, Eq)]
187pub struct MetadataPartitionV12 {
188 pub error_code: i16,
189 pub partition_index: i32,
190 pub leader_id: i32,
191 pub leader_epoch: i32,
192 pub replica_nodes: Vec<i32>,
193 pub isr_nodes: Vec<i32>,
194 pub offline_replicas: Vec<i32>,
195}
196
197impl MetadataPartitionV12 {
198 fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
199 let partition = Self {
200 error_code: decoder.read_i16()?,
201 partition_index: decoder.read_i32()?,
202 leader_id: decoder.read_i32()?,
203 leader_epoch: decoder.read_i32()?,
204 replica_nodes: decoder
205 .read_compact_array("metadata v12 replica nodes", |decoder| decoder.read_i32())?
206 .unwrap_or_default(),
207 isr_nodes: decoder
208 .read_compact_array("metadata v12 isr nodes", |decoder| decoder.read_i32())?
209 .unwrap_or_default(),
210 offline_replicas: decoder
211 .read_compact_array("metadata v12 offline replicas", |decoder| {
212 decoder.read_i32()
213 })?
214 .unwrap_or_default(),
215 };
216 decoder.read_tagged_fields()?;
217 Ok(partition)
218 }
219}
220
221#[derive(Debug, Clone, PartialEq, Eq)]
223pub struct MetadataTopicV12 {
224 pub error_code: i16,
225 pub name: Option<String>,
226 pub topic_id: [u8; 16],
227 pub is_internal: bool,
228 pub partitions: Vec<MetadataPartitionV12>,
229 pub topic_authorized_operations: i32,
230}
231
232impl MetadataTopicV12 {
233 fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
234 let topic = Self {
235 error_code: decoder.read_i16()?,
236 name: decoder.read_compact_nullable_string()?,
237 topic_id: decoder.read_uuid()?,
238 is_internal: decoder.read_bool()?,
239 partitions: decoder
240 .read_compact_array("metadata v12 partitions", MetadataPartitionV12::decode)?
241 .unwrap_or_default(),
242 topic_authorized_operations: decoder.read_i32()?,
243 };
244 decoder.read_tagged_fields()?;
245 Ok(topic)
246 }
247}
248
249#[derive(Debug, Clone, PartialEq, Eq)]
251pub struct MetadataResponseV12 {
252 pub throttle_time_ms: i32,
253 pub brokers: Vec<MetadataBrokerV12>,
254 pub cluster_id: Option<String>,
255 pub controller_id: i32,
256 pub topics: Vec<MetadataTopicV12>,
257}
258
259impl MetadataResponseV12 {
260 pub fn decode_body(decoder: &mut Decoder<'_>) -> Result<Self> {
261 let response = Self {
262 throttle_time_ms: decoder.read_i32()?,
263 brokers: decoder
264 .read_compact_array("metadata v12 brokers", MetadataBrokerV12::decode)?
265 .unwrap_or_default(),
266 cluster_id: decoder.read_compact_nullable_string()?,
267 controller_id: decoder.read_i32()?,
268 topics: decoder
269 .read_compact_array("metadata v12 topics", MetadataTopicV12::decode)?
270 .unwrap_or_default(),
271 };
272 decoder.read_tagged_fields()?;
273 Ok(response)
274 }
275}
276
277#[cfg(test)]
278#[allow(clippy::unwrap_used)]
279mod tests {
280 use super::{
281 MetadataRequestTopicV12, MetadataRequestV1, MetadataRequestV12, MetadataResponseV1, API_KEY,
282 };
283 use crate::codec::{Decoder, Encoder};
284
285 #[test]
286 fn encodes_metadata_request_v1_for_topics() {
287 let request = MetadataRequestV1 {
288 correlation_id: 9,
289 client_id: Some("kafrust".to_owned()),
290 topics: Some(vec!["orders".to_owned()]),
291 };
292
293 assert_eq!(
294 request.encode().unwrap(),
295 [
296 0, 3, 0, 1, 0, 0, 0, 9, 0, 7, b'k', b'a', b'f', b'r', b'u', b's', b't', 0, 0, 0, 1, 0, 6, b'o', b'r', b'd', b'e', b'r', b's', ]
303 );
304 assert_eq!(API_KEY, 3);
305 }
306
307 #[test]
308 fn encodes_metadata_request_v1_for_all_topics() {
309 let request = MetadataRequestV1 {
310 correlation_id: 9,
311 client_id: None,
312 topics: None,
313 };
314
315 assert_eq!(
316 request.encode().unwrap(),
317 [
318 0, 3, 0, 1, 0, 0, 0, 9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ]
324 );
325 }
326
327 #[test]
328 fn encodes_metadata_request_v12_with_topic_name_selector() {
329 let request = MetadataRequestV12 {
330 correlation_id: 11,
331 client_id: Some("kafrust".to_owned()),
332 topics: Some(vec![MetadataRequestTopicV12 {
333 topic_id: [0; 16],
334 name: Some("orders".to_owned()),
335 }]),
336 allow_auto_topic_creation: false,
337 include_topic_authorized_operations: false,
338 };
339
340 let encoded = request.encode().unwrap();
341 assert_eq!(&encoded[0..4], &[0, 3, 0, 12]);
342 assert_eq!(&encoded[4..8], &[0, 0, 0, 11]);
343 assert!(encoded.windows(6).any(|bytes| bytes == b"orders"));
344 assert!(encoded.ends_with(&[0, 0, 0]));
345 }
346
347 #[test]
348 fn decodes_metadata_response_v12_with_topic_uuid() {
349 let mut bytes = Encoder::new();
350 bytes.write_i32(0);
351 bytes
352 .write_compact_array(
353 Some(&[super::MetadataBrokerV12 {
354 node_id: 1,
355 host: "localhost".to_owned(),
356 port: 9092,
357 rack: None,
358 }]),
359 |encoder, broker| {
360 encoder.write_i32(broker.node_id);
361 encoder.write_compact_string(&broker.host)?;
362 encoder.write_i32(broker.port);
363 encoder.write_compact_nullable_string(broker.rack.as_deref())?;
364 encoder.write_empty_tagged_fields();
365 Ok(())
366 },
367 )
368 .unwrap();
369 bytes
370 .write_compact_nullable_string(Some("cluster"))
371 .unwrap();
372 bytes.write_i32(1);
373 bytes
374 .write_compact_array(
375 Some(&[super::MetadataTopicV12 {
376 error_code: 0,
377 name: Some("orders".to_owned()),
378 topic_id: [7; 16],
379 is_internal: false,
380 partitions: vec![super::MetadataPartitionV12 {
381 error_code: 0,
382 partition_index: 0,
383 leader_id: 1,
384 leader_epoch: 3,
385 replica_nodes: vec![1],
386 isr_nodes: vec![1],
387 offline_replicas: Vec::new(),
388 }],
389 topic_authorized_operations: -2147483648,
390 }]),
391 |encoder, topic| {
392 encoder.write_i16(topic.error_code);
393 encoder.write_compact_nullable_string(topic.name.as_deref())?;
394 encoder.write_uuid(&topic.topic_id);
395 encoder.write_bool(topic.is_internal);
396 encoder.write_compact_array(
397 Some(&topic.partitions),
398 |encoder, partition| {
399 encoder.write_i16(partition.error_code);
400 encoder.write_i32(partition.partition_index);
401 encoder.write_i32(partition.leader_id);
402 encoder.write_i32(partition.leader_epoch);
403 encoder.write_compact_array(
404 Some(&partition.replica_nodes),
405 |encoder, value| {
406 encoder.write_i32(*value);
407 Ok(())
408 },
409 )?;
410 encoder.write_compact_array(
411 Some(&partition.isr_nodes),
412 |encoder, value| {
413 encoder.write_i32(*value);
414 Ok(())
415 },
416 )?;
417 encoder.write_compact_array(
418 Some(&partition.offline_replicas),
419 |encoder, value| {
420 encoder.write_i32(*value);
421 Ok(())
422 },
423 )?;
424 encoder.write_empty_tagged_fields();
425 Ok(())
426 },
427 )?;
428 encoder.write_i32(topic.topic_authorized_operations);
429 encoder.write_empty_tagged_fields();
430 Ok(())
431 },
432 )
433 .unwrap();
434 bytes.write_empty_tagged_fields();
435
436 let bytes = bytes.into_bytes();
437 let mut decoder = Decoder::new(&bytes);
438 let response = super::MetadataResponseV12::decode_body(&mut decoder).unwrap();
439
440 assert_eq!(response.cluster_id.as_deref(), Some("cluster"));
441 assert_eq!(response.topics[0].name.as_deref(), Some("orders"));
442 assert_eq!(response.topics[0].topic_id, [7; 16]);
443 assert_eq!(response.topics[0].partitions[0].leader_epoch, 3);
444 assert!(decoder.is_empty());
445 }
446
447 #[test]
448 fn decodes_metadata_response_v1() {
449 let bytes = [
450 0, 0, 0, 1, 0, 0, 0, 1, 0, 9, b'l', b'o', b'c', b'a', b'l', b'h', b'o', b's', b't', 0, 0, 35, 132, 0xff, 0xff, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 6, b'o', b'r', b'd', b'e', b'r', b's', 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, ];
469
470 let mut decoder = Decoder::new(&bytes);
471 let response = MetadataResponseV1::decode_body(&mut decoder).unwrap();
472
473 assert_eq!(response.controller_id, 1);
474 assert_eq!(response.brokers.len(), 1);
475 assert_eq!(response.brokers[0].host, "localhost");
476 assert_eq!(response.brokers[0].port, 9092);
477 assert_eq!(response.topics.len(), 1);
478 assert_eq!(response.topics[0].name, "orders");
479 assert_eq!(response.topics[0].partitions[0].leader_id, 1);
480 assert_eq!(response.topics[0].partitions[0].replica_nodes, vec![1]);
481 assert_eq!(response.topics[0].partitions[0].isr_nodes, vec![1]);
482 assert!(decoder.is_empty());
483 }
484}