Skip to main content

kafrust_protocol/api/
fetch.rs

1use crate::api::produce::RecordBatchHeader;
2use crate::codec::{DecodeLimits, Decoder, Encoder};
3use crate::error::{Error, Result};
4use crate::header::RequestHeader;
5use crate::record_batch::{decompress_record_batch_records_with_limit, RecordBatchCompression};
6
7pub const API_KEY: i16 = 1;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct FetchRequestV2 {
11    pub correlation_id: i32,
12    pub client_id: Option<String>,
13    pub replica_id: i32,
14    pub max_wait_ms: i32,
15    pub min_bytes: i32,
16    pub topics: Vec<FetchTopicV2>,
17}
18
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct FetchRequestV4 {
21    pub correlation_id: i32,
22    pub client_id: Option<String>,
23    pub replica_id: i32,
24    pub max_wait_ms: i32,
25    pub min_bytes: i32,
26    pub max_bytes: i32,
27    pub isolation_level: i8,
28    pub topics: Vec<FetchTopicV2>,
29}
30
31/// Fetch request version 11 with rack-aware read selection.
32///
33/// Version 11 keeps the non-flexible wire format used by the existing direct
34/// consumer while adding fetch-session fields and the consumer rack ID.
35#[derive(Debug, Clone, PartialEq, Eq)]
36pub struct FetchRequestV11 {
37    pub correlation_id: i32,
38    pub client_id: Option<String>,
39    pub replica_id: i32,
40    pub max_wait_ms: i32,
41    pub min_bytes: i32,
42    pub max_bytes: i32,
43    pub isolation_level: i8,
44    pub session_id: i32,
45    pub session_epoch: i32,
46    pub topics: Vec<FetchTopicV11>,
47    pub forgotten_topics: Vec<FetchForgottenTopicV11>,
48    pub rack_id: String,
49}
50
51/// Fetch request version 12 with flexible encoding and rack-aware read selection.
52#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct FetchRequestV12 {
54    pub correlation_id: i32,
55    pub client_id: Option<String>,
56    pub replica_id: i32,
57    pub max_wait_ms: i32,
58    pub min_bytes: i32,
59    pub max_bytes: i32,
60    pub isolation_level: i8,
61    pub session_id: i32,
62    pub session_epoch: i32,
63    pub topics: Vec<FetchTopicV12>,
64    pub forgotten_topics: Vec<FetchForgottenTopicV12>,
65    pub rack_id: String,
66}
67
68/// Fetch request version 13 with topic UUIDs (KIP-516).
69///
70/// Version 13 adds topic UUIDs but retains the legacy top-level `ReplicaId`
71/// field. The optional cluster ID is represented as tag 0; omitting it is the
72/// normal consumer request shape. Kafka moves replica identity into the
73/// tagged `ReplicaState` structure starting with version 15.
74#[derive(Debug, Clone, PartialEq, Eq)]
75pub struct FetchRequestV13 {
76    pub correlation_id: i32,
77    pub client_id: Option<String>,
78    pub cluster_id: Option<String>,
79    pub replica_id: i32,
80    pub max_wait_ms: i32,
81    pub min_bytes: i32,
82    pub max_bytes: i32,
83    pub isolation_level: i8,
84    pub session_id: i32,
85    pub session_epoch: i32,
86    pub topics: Vec<FetchTopicV13>,
87    pub forgotten_topics: Vec<FetchForgottenTopicV13>,
88    pub rack_id: String,
89}
90
91impl FetchRequestV12 {
92    pub fn encode(&self) -> Result<Vec<u8>> {
93        let mut encoder = Encoder::new();
94        RequestHeader {
95            api_key: API_KEY,
96            api_version: 12,
97            correlation_id: self.correlation_id,
98            client_id: self.client_id.clone(),
99        }
100        .encode_v2(&mut encoder)?;
101        encoder.write_i32(self.replica_id);
102        encoder.write_i32(self.max_wait_ms);
103        encoder.write_i32(self.min_bytes);
104        encoder.write_i32(self.max_bytes);
105        encoder.write_i8(self.isolation_level);
106        encoder.write_i32(self.session_id);
107        encoder.write_i32(self.session_epoch);
108        encoder.write_compact_array(Some(self.topics.as_slice()), |encoder, topic| {
109            topic.encode(encoder)
110        })?;
111        encoder.write_compact_array(Some(self.forgotten_topics.as_slice()), |encoder, topic| {
112            topic.encode(encoder)
113        })?;
114        encoder.write_compact_string(&self.rack_id)?;
115        encoder.write_empty_tagged_fields();
116        Ok(encoder.into_bytes())
117    }
118}
119
120impl FetchRequestV13 {
121    pub fn encode(&self) -> Result<Vec<u8>> {
122        self.encode_version(13)
123    }
124
125    fn encode_version(&self, api_version: i16) -> Result<Vec<u8>> {
126        let mut encoder = Encoder::new();
127        RequestHeader {
128            api_key: API_KEY,
129            api_version,
130            correlation_id: self.correlation_id,
131            client_id: self.client_id.clone(),
132        }
133        .encode_v2(&mut encoder)?;
134        encoder.write_i32(self.replica_id);
135        encoder.write_i32(self.max_wait_ms);
136        encoder.write_i32(self.min_bytes);
137        encoder.write_i32(self.max_bytes);
138        encoder.write_i8(self.isolation_level);
139        encoder.write_i32(self.session_id);
140        encoder.write_i32(self.session_epoch);
141        encoder.write_compact_array(Some(self.topics.as_slice()), |encoder, topic| {
142            topic.encode(encoder)
143        })?;
144        encoder.write_compact_array(Some(self.forgotten_topics.as_slice()), |encoder, topic| {
145            topic.encode(encoder)
146        })?;
147        encoder.write_compact_string(&self.rack_id)?;
148        write_cluster_id_tag(&mut encoder, self.cluster_id.as_deref())?;
149        Ok(encoder.into_bytes())
150    }
151}
152
153/// Fetch request version 14. The v14 request wire shape is identical to v13;
154/// Kafka 4.x adds a tiered-storage error to the response contract.
155#[derive(Debug, Clone, PartialEq, Eq)]
156pub struct FetchRequestV14 {
157    pub correlation_id: i32,
158    pub client_id: Option<String>,
159    pub cluster_id: Option<String>,
160    pub replica_id: i32,
161    pub max_wait_ms: i32,
162    pub min_bytes: i32,
163    pub max_bytes: i32,
164    pub isolation_level: i8,
165    pub session_id: i32,
166    pub session_epoch: i32,
167    pub topics: Vec<FetchTopicV14>,
168    pub forgotten_topics: Vec<FetchForgottenTopicV14>,
169    pub rack_id: String,
170}
171
172impl FetchRequestV14 {
173    pub fn encode(&self) -> Result<Vec<u8>> {
174        FetchRequestV13 {
175            correlation_id: self.correlation_id,
176            client_id: self.client_id.clone(),
177            cluster_id: self.cluster_id.clone(),
178            replica_id: self.replica_id,
179            max_wait_ms: self.max_wait_ms,
180            min_bytes: self.min_bytes,
181            max_bytes: self.max_bytes,
182            isolation_level: self.isolation_level,
183            session_id: self.session_id,
184            session_epoch: self.session_epoch,
185            topics: self.topics.clone(),
186            forgotten_topics: self.forgotten_topics.clone(),
187            rack_id: self.rack_id.clone(),
188        }
189        .encode_version(14)
190    }
191}
192
193/// Replica state carried by Fetch request versions 15 and newer.
194#[derive(Debug, Clone, PartialEq, Eq)]
195pub struct FetchReplicaStateV15 {
196    pub replica_id: i32,
197    pub replica_epoch: i64,
198}
199
200/// Fetch request version 15 with the KIP-903 replica state.
201#[derive(Debug, Clone, PartialEq, Eq)]
202pub struct FetchRequestV15 {
203    pub correlation_id: i32,
204    pub client_id: Option<String>,
205    pub cluster_id: Option<String>,
206    pub replica_state: Option<FetchReplicaStateV15>,
207    pub max_wait_ms: i32,
208    pub min_bytes: i32,
209    pub max_bytes: i32,
210    pub isolation_level: i8,
211    pub session_id: i32,
212    pub session_epoch: i32,
213    pub topics: Vec<FetchTopicV15>,
214    pub forgotten_topics: Vec<FetchForgottenTopicV15>,
215    pub rack_id: String,
216}
217
218impl FetchRequestV15 {
219    pub fn encode(&self) -> Result<Vec<u8>> {
220        self.encode_version(15)
221    }
222
223    fn encode_version(&self, api_version: i16) -> Result<Vec<u8>> {
224        let mut encoder = Encoder::new();
225        RequestHeader {
226            api_key: API_KEY,
227            api_version,
228            correlation_id: self.correlation_id,
229            client_id: self.client_id.clone(),
230        }
231        .encode_v2(&mut encoder)?;
232        encoder.write_i32(self.max_wait_ms);
233        encoder.write_i32(self.min_bytes);
234        encoder.write_i32(self.max_bytes);
235        encoder.write_i8(self.isolation_level);
236        encoder.write_i32(self.session_id);
237        encoder.write_i32(self.session_epoch);
238        encoder.write_compact_array(Some(self.topics.as_slice()), |encoder, topic| {
239            topic.encode(encoder)
240        })?;
241        encoder.write_compact_array(Some(self.forgotten_topics.as_slice()), |encoder, topic| {
242            topic.encode(encoder)
243        })?;
244        encoder.write_compact_string(&self.rack_id)?;
245        write_fetch_request_tags(
246            &mut encoder,
247            self.cluster_id.as_deref(),
248            self.replica_state.as_ref(),
249        )?;
250        Ok(encoder.into_bytes())
251    }
252}
253
254/// Fetch request version 16. Kafka 4.3 keeps the v15 request shape.
255#[derive(Debug, Clone, PartialEq, Eq)]
256pub struct FetchRequestV16 {
257    pub correlation_id: i32,
258    pub client_id: Option<String>,
259    pub cluster_id: Option<String>,
260    pub replica_state: Option<FetchReplicaStateV15>,
261    pub max_wait_ms: i32,
262    pub min_bytes: i32,
263    pub max_bytes: i32,
264    pub isolation_level: i8,
265    pub session_id: i32,
266    pub session_epoch: i32,
267    pub topics: Vec<FetchTopicV16>,
268    pub forgotten_topics: Vec<FetchForgottenTopicV16>,
269    pub rack_id: String,
270}
271
272impl FetchRequestV16 {
273    pub fn encode(&self) -> Result<Vec<u8>> {
274        FetchRequestV15 {
275            correlation_id: self.correlation_id,
276            client_id: self.client_id.clone(),
277            cluster_id: self.cluster_id.clone(),
278            replica_state: self.replica_state.clone(),
279            max_wait_ms: self.max_wait_ms,
280            min_bytes: self.min_bytes,
281            max_bytes: self.max_bytes,
282            isolation_level: self.isolation_level,
283            session_id: self.session_id,
284            session_epoch: self.session_epoch,
285            topics: self.topics.clone(),
286            forgotten_topics: self.forgotten_topics.clone(),
287            rack_id: self.rack_id.clone(),
288        }
289        .encode_version(16)
290    }
291}
292
293/// Fetch request version 17 adds the optional follower directory ID.
294#[derive(Debug, Clone, PartialEq, Eq)]
295pub struct FetchRequestV17 {
296    pub correlation_id: i32,
297    pub client_id: Option<String>,
298    pub cluster_id: Option<String>,
299    pub replica_state: Option<FetchReplicaStateV15>,
300    pub max_wait_ms: i32,
301    pub min_bytes: i32,
302    pub max_bytes: i32,
303    pub isolation_level: i8,
304    pub session_id: i32,
305    pub session_epoch: i32,
306    pub topics: Vec<FetchTopicV17>,
307    pub forgotten_topics: Vec<FetchForgottenTopicV17>,
308    pub rack_id: String,
309}
310
311impl FetchRequestV17 {
312    pub fn encode(&self) -> Result<Vec<u8>> {
313        encode_fetch_v17_plus_request(
314            17,
315            self.correlation_id,
316            self.client_id.clone(),
317            self.cluster_id.as_deref(),
318            self.replica_state.as_ref(),
319            self.max_wait_ms,
320            self.min_bytes,
321            self.max_bytes,
322            self.isolation_level,
323            self.session_id,
324            self.session_epoch,
325            &self.topics,
326            |encoder, topic| topic.encode(encoder),
327            &self.forgotten_topics,
328            self.rack_id.as_str(),
329        )
330    }
331}
332
333/// Fetch request version 18 adds the follower high-watermark tagged field.
334#[derive(Debug, Clone, PartialEq, Eq)]
335pub struct FetchRequestV18 {
336    pub correlation_id: i32,
337    pub client_id: Option<String>,
338    pub cluster_id: Option<String>,
339    pub replica_state: Option<FetchReplicaStateV15>,
340    pub max_wait_ms: i32,
341    pub min_bytes: i32,
342    pub max_bytes: i32,
343    pub isolation_level: i8,
344    pub session_id: i32,
345    pub session_epoch: i32,
346    pub topics: Vec<FetchTopicV18>,
347    pub forgotten_topics: Vec<FetchForgottenTopicV18>,
348    pub rack_id: String,
349}
350
351impl FetchRequestV18 {
352    pub fn encode(&self) -> Result<Vec<u8>> {
353        encode_fetch_v17_plus_request(
354            18,
355            self.correlation_id,
356            self.client_id.clone(),
357            self.cluster_id.as_deref(),
358            self.replica_state.as_ref(),
359            self.max_wait_ms,
360            self.min_bytes,
361            self.max_bytes,
362            self.isolation_level,
363            self.session_id,
364            self.session_epoch,
365            &self.topics,
366            |encoder, topic| topic.encode(encoder),
367            &self.forgotten_topics,
368            self.rack_id.as_str(),
369        )
370    }
371}
372
373// Keep the Kafka wire fields explicit here so v17/v18 encoding stays easy to audit.
374#[allow(clippy::too_many_arguments)]
375fn encode_fetch_v17_plus_request<T, F>(
376    api_version: i16,
377    correlation_id: i32,
378    client_id: Option<String>,
379    cluster_id: Option<&str>,
380    replica_state: Option<&FetchReplicaStateV15>,
381    max_wait_ms: i32,
382    min_bytes: i32,
383    max_bytes: i32,
384    isolation_level: i8,
385    session_id: i32,
386    session_epoch: i32,
387    topics: &[T],
388    mut encode_topic: F,
389    forgotten_topics: &[FetchForgottenTopicV13],
390    rack_id: &str,
391) -> Result<Vec<u8>>
392where
393    F: FnMut(&mut Encoder, &T) -> Result<()>,
394{
395    let mut encoder = Encoder::new();
396    RequestHeader {
397        api_key: API_KEY,
398        api_version,
399        correlation_id,
400        client_id,
401    }
402    .encode_v2(&mut encoder)?;
403    encoder.write_i32(max_wait_ms);
404    encoder.write_i32(min_bytes);
405    encoder.write_i32(max_bytes);
406    encoder.write_i8(isolation_level);
407    encoder.write_i32(session_id);
408    encoder.write_i32(session_epoch);
409    encoder.write_compact_array(Some(topics), |encoder, topic| encode_topic(encoder, topic))?;
410    encoder.write_compact_array(Some(forgotten_topics), |encoder, topic| {
411        topic.encode(encoder)
412    })?;
413    encoder.write_compact_string(rack_id)?;
414    write_fetch_request_tags(&mut encoder, cluster_id, replica_state)?;
415    Ok(encoder.into_bytes())
416}
417
418fn write_cluster_id_tag(encoder: &mut Encoder, cluster_id: Option<&str>) -> Result<()> {
419    write_fetch_request_tags(encoder, cluster_id, None)
420}
421
422fn write_fetch_request_tags(
423    encoder: &mut Encoder,
424    cluster_id: Option<&str>,
425    replica_state: Option<&FetchReplicaStateV15>,
426) -> Result<()> {
427    let mut fields = Vec::new();
428    if let Some(cluster_id) = cluster_id {
429        let mut value = Encoder::new();
430        value.write_compact_string(cluster_id)?;
431        fields.push((0, value.into_bytes()));
432    }
433    if let Some(replica_state) = replica_state {
434        let mut value = Encoder::new();
435        value.write_i32(replica_state.replica_id);
436        value.write_i64(replica_state.replica_epoch);
437        fields.push((1, value.into_bytes()));
438    }
439    let field_count =
440        u32::try_from(fields.len()).map_err(|_| Error::LengthOverflow("tagged fields"))?;
441    encoder.write_unsigned_varint(field_count);
442    for (tag, value) in fields {
443        encoder.write_unsigned_varint(tag);
444        let value_len =
445            u32::try_from(value.len()).map_err(|_| Error::LengthOverflow("tagged field"))?;
446        encoder.write_unsigned_varint(value_len);
447        encoder.write_raw(&value);
448    }
449    Ok(())
450}
451
452fn write_fetch_partition_v17_tags(
453    encoder: &mut Encoder,
454    replica_directory_id: Option<&[u8; 16]>,
455    high_watermark: Option<i64>,
456) {
457    let tag_count = u32::from(replica_directory_id.is_some()) + u32::from(high_watermark.is_some());
458    encoder.write_unsigned_varint(tag_count);
459    if let Some(replica_directory_id) = replica_directory_id {
460        encoder.write_unsigned_varint(0);
461        encoder.write_unsigned_varint(16);
462        encoder.write_uuid(replica_directory_id);
463    }
464    if let Some(high_watermark) = high_watermark {
465        encoder.write_unsigned_varint(1);
466        encoder.write_unsigned_varint(8);
467        encoder.write_i64(high_watermark);
468    }
469}
470
471#[derive(Debug, Clone, PartialEq, Eq)]
472pub struct FetchTopicV12 {
473    pub name: String,
474    pub partitions: Vec<FetchPartitionV12>,
475}
476
477impl FetchTopicV12 {
478    fn encode(&self, encoder: &mut Encoder) -> Result<()> {
479        encoder.write_compact_string(&self.name)?;
480        encoder.write_compact_array(Some(self.partitions.as_slice()), |encoder, partition| {
481            partition.encode(encoder)
482        })?;
483        encoder.write_empty_tagged_fields();
484        Ok(())
485    }
486}
487
488#[derive(Debug, Clone, PartialEq, Eq)]
489pub struct FetchPartitionV12 {
490    pub partition_index: i32,
491    pub current_leader_epoch: i32,
492    pub fetch_offset: i64,
493    pub last_fetched_epoch: i32,
494    pub log_start_offset: i64,
495    pub max_bytes: i32,
496}
497
498impl FetchPartitionV12 {
499    fn encode(&self, encoder: &mut Encoder) -> Result<()> {
500        encoder.write_i32(self.partition_index);
501        encoder.write_i32(self.current_leader_epoch);
502        encoder.write_i64(self.fetch_offset);
503        encoder.write_i32(self.last_fetched_epoch);
504        encoder.write_i64(self.log_start_offset);
505        encoder.write_i32(self.max_bytes);
506        encoder.write_empty_tagged_fields();
507        Ok(())
508    }
509}
510
511#[derive(Debug, Clone, PartialEq, Eq)]
512pub struct FetchForgottenTopicV12 {
513    pub name: String,
514    pub partitions: Vec<i32>,
515}
516
517#[derive(Debug, Clone, PartialEq, Eq)]
518pub struct FetchTopicV13 {
519    pub topic_id: [u8; 16],
520    pub partitions: Vec<FetchPartitionV13>,
521}
522
523impl FetchTopicV13 {
524    fn encode(&self, encoder: &mut Encoder) -> Result<()> {
525        encoder.write_uuid(&self.topic_id);
526        encoder.write_compact_array(Some(self.partitions.as_slice()), |encoder, partition| {
527            partition.encode(encoder)
528        })?;
529        encoder.write_empty_tagged_fields();
530        Ok(())
531    }
532}
533
534pub type FetchPartitionV13 = FetchPartitionV12;
535pub type FetchTopicV14 = FetchTopicV13;
536pub type FetchPartitionV14 = FetchPartitionV13;
537pub type FetchTopicV16 = FetchTopicV13;
538pub type FetchPartitionV16 = FetchPartitionV13;
539pub type FetchTopicV15 = FetchTopicV13;
540pub type FetchPartitionV15 = FetchPartitionV13;
541
542#[derive(Debug, Clone, PartialEq, Eq)]
543pub struct FetchTopicV17 {
544    pub topic_id: [u8; 16],
545    pub partitions: Vec<FetchPartitionV17>,
546}
547
548impl FetchTopicV17 {
549    fn encode(&self, encoder: &mut Encoder) -> Result<()> {
550        encoder.write_uuid(&self.topic_id);
551        encoder.write_compact_array(Some(self.partitions.as_slice()), |encoder, partition| {
552            partition.encode(encoder)
553        })?;
554        encoder.write_empty_tagged_fields();
555        Ok(())
556    }
557}
558
559#[derive(Debug, Clone, PartialEq, Eq)]
560pub struct FetchPartitionV17 {
561    pub partition_index: i32,
562    pub current_leader_epoch: i32,
563    pub fetch_offset: i64,
564    pub last_fetched_epoch: i32,
565    pub log_start_offset: i64,
566    pub max_bytes: i32,
567    pub replica_directory_id: Option<[u8; 16]>,
568}
569
570impl FetchPartitionV17 {
571    fn encode(&self, encoder: &mut Encoder) -> Result<()> {
572        encoder.write_i32(self.partition_index);
573        encoder.write_i32(self.current_leader_epoch);
574        encoder.write_i64(self.fetch_offset);
575        encoder.write_i32(self.last_fetched_epoch);
576        encoder.write_i64(self.log_start_offset);
577        encoder.write_i32(self.max_bytes);
578        write_fetch_partition_v17_tags(encoder, self.replica_directory_id.as_ref(), None);
579        Ok(())
580    }
581}
582
583pub type FetchForgottenTopicV16 = FetchForgottenTopicV13;
584pub type FetchForgottenTopicV17 = FetchForgottenTopicV13;
585
586#[derive(Debug, Clone, PartialEq, Eq)]
587pub struct FetchTopicV18 {
588    pub topic_id: [u8; 16],
589    pub partitions: Vec<FetchPartitionV18>,
590}
591
592impl FetchTopicV18 {
593    fn encode(&self, encoder: &mut Encoder) -> Result<()> {
594        encoder.write_uuid(&self.topic_id);
595        encoder.write_compact_array(Some(self.partitions.as_slice()), |encoder, partition| {
596            partition.encode(encoder)
597        })?;
598        encoder.write_empty_tagged_fields();
599        Ok(())
600    }
601}
602
603#[derive(Debug, Clone, PartialEq, Eq)]
604pub struct FetchPartitionV18 {
605    pub partition_index: i32,
606    pub current_leader_epoch: i32,
607    pub fetch_offset: i64,
608    pub last_fetched_epoch: i32,
609    pub log_start_offset: i64,
610    pub max_bytes: i32,
611    pub replica_directory_id: Option<[u8; 16]>,
612    pub high_watermark: Option<i64>,
613}
614
615impl FetchPartitionV18 {
616    fn encode(&self, encoder: &mut Encoder) -> Result<()> {
617        encoder.write_i32(self.partition_index);
618        encoder.write_i32(self.current_leader_epoch);
619        encoder.write_i64(self.fetch_offset);
620        encoder.write_i32(self.last_fetched_epoch);
621        encoder.write_i64(self.log_start_offset);
622        encoder.write_i32(self.max_bytes);
623        write_fetch_partition_v17_tags(
624            encoder,
625            self.replica_directory_id.as_ref(),
626            self.high_watermark,
627        );
628        Ok(())
629    }
630}
631
632pub type FetchForgottenTopicV18 = FetchForgottenTopicV13;
633
634#[derive(Debug, Clone, PartialEq, Eq)]
635pub struct FetchForgottenTopicV13 {
636    pub topic_id: [u8; 16],
637    pub partitions: Vec<i32>,
638}
639
640impl FetchForgottenTopicV13 {
641    fn encode(&self, encoder: &mut Encoder) -> Result<()> {
642        encoder.write_uuid(&self.topic_id);
643        encoder.write_compact_array(Some(self.partitions.as_slice()), |encoder, partition| {
644            encoder.write_i32(*partition);
645            Ok(())
646        })?;
647        encoder.write_empty_tagged_fields();
648        Ok(())
649    }
650}
651
652impl FetchForgottenTopicV12 {
653    fn encode(&self, encoder: &mut Encoder) -> Result<()> {
654        encoder.write_compact_string(&self.name)?;
655        encoder.write_compact_array(Some(self.partitions.as_slice()), |encoder, partition| {
656            encoder.write_i32(*partition);
657            Ok(())
658        })?;
659        encoder.write_empty_tagged_fields();
660        Ok(())
661    }
662}
663
664impl FetchRequestV11 {
665    pub fn encode(&self) -> Result<Vec<u8>> {
666        let mut encoder = Encoder::new();
667        RequestHeader {
668            api_key: API_KEY,
669            api_version: 11,
670            correlation_id: self.correlation_id,
671            client_id: self.client_id.clone(),
672        }
673        .encode_v1(&mut encoder)?;
674        encoder.write_i32(self.replica_id);
675        encoder.write_i32(self.max_wait_ms);
676        encoder.write_i32(self.min_bytes);
677        encoder.write_i32(self.max_bytes);
678        encoder.write_i8(self.isolation_level);
679        encoder.write_i32(self.session_id);
680        encoder.write_i32(self.session_epoch);
681        encoder.write_array(Some(self.topics.as_slice()), |encoder, topic| {
682            topic.encode(encoder)
683        })?;
684        encoder.write_array(Some(self.forgotten_topics.as_slice()), |encoder, topic| {
685            topic.encode(encoder)
686        })?;
687        encoder.write_string(&self.rack_id)?;
688        Ok(encoder.into_bytes())
689    }
690}
691
692#[derive(Debug, Clone, PartialEq, Eq)]
693pub struct FetchTopicV11 {
694    pub name: String,
695    pub partitions: Vec<FetchPartitionV11>,
696}
697
698impl FetchTopicV11 {
699    fn encode(&self, encoder: &mut Encoder) -> Result<()> {
700        encoder.write_string(&self.name)?;
701        encoder.write_array(Some(self.partitions.as_slice()), |encoder, partition| {
702            partition.encode(encoder)
703        })
704    }
705}
706
707#[derive(Debug, Clone, PartialEq, Eq)]
708pub struct FetchPartitionV11 {
709    pub partition_index: i32,
710    pub current_leader_epoch: i32,
711    pub fetch_offset: i64,
712    pub log_start_offset: i64,
713    pub max_bytes: i32,
714}
715
716impl FetchPartitionV11 {
717    fn encode(&self, encoder: &mut Encoder) -> Result<()> {
718        encoder.write_i32(self.partition_index);
719        encoder.write_i32(self.current_leader_epoch);
720        encoder.write_i64(self.fetch_offset);
721        encoder.write_i64(self.log_start_offset);
722        encoder.write_i32(self.max_bytes);
723        Ok(())
724    }
725}
726
727#[derive(Debug, Clone, PartialEq, Eq)]
728pub struct FetchForgottenTopicV11 {
729    pub name: String,
730    pub partitions: Vec<i32>,
731}
732
733impl FetchForgottenTopicV11 {
734    fn encode(&self, encoder: &mut Encoder) -> Result<()> {
735        encoder.write_string(&self.name)?;
736        encoder.write_array(Some(self.partitions.as_slice()), |encoder, partition| {
737            encoder.write_i32(*partition);
738            Ok(())
739        })
740    }
741}
742
743impl FetchRequestV4 {
744    pub fn encode(&self) -> Result<Vec<u8>> {
745        let mut encoder = Encoder::new();
746        RequestHeader {
747            api_key: API_KEY,
748            api_version: 4,
749            correlation_id: self.correlation_id,
750            client_id: self.client_id.clone(),
751        }
752        .encode_v1(&mut encoder)?;
753        encoder.write_i32(self.replica_id);
754        encoder.write_i32(self.max_wait_ms);
755        encoder.write_i32(self.min_bytes);
756        encoder.write_i32(self.max_bytes);
757        encoder.write_i8(self.isolation_level);
758        encoder.write_array(Some(self.topics.as_slice()), |encoder, topic| {
759            topic.encode(encoder)
760        })?;
761        Ok(encoder.into_bytes())
762    }
763}
764
765impl FetchRequestV2 {
766    pub fn encode(&self) -> Result<Vec<u8>> {
767        let mut encoder = Encoder::new();
768        RequestHeader {
769            api_key: API_KEY,
770            api_version: 2,
771            correlation_id: self.correlation_id,
772            client_id: self.client_id.clone(),
773        }
774        .encode_v1(&mut encoder)?;
775        encoder.write_i32(self.replica_id);
776        encoder.write_i32(self.max_wait_ms);
777        encoder.write_i32(self.min_bytes);
778        encoder.write_array(Some(self.topics.as_slice()), |encoder, topic| {
779            topic.encode(encoder)
780        })?;
781        Ok(encoder.into_bytes())
782    }
783}
784
785#[derive(Debug, Clone, PartialEq, Eq)]
786pub struct FetchTopicV2 {
787    pub name: String,
788    pub partitions: Vec<FetchPartitionV2>,
789}
790
791impl FetchTopicV2 {
792    fn encode(&self, encoder: &mut Encoder) -> Result<()> {
793        encoder.write_string(&self.name)?;
794        encoder.write_array(Some(self.partitions.as_slice()), |encoder, partition| {
795            partition.encode(encoder)
796        })
797    }
798}
799
800#[derive(Debug, Clone, PartialEq, Eq)]
801pub struct FetchPartitionV2 {
802    pub partition_index: i32,
803    pub fetch_offset: i64,
804    pub max_bytes: i32,
805}
806
807impl FetchPartitionV2 {
808    fn encode(&self, encoder: &mut Encoder) -> Result<()> {
809        encoder.write_i32(self.partition_index);
810        encoder.write_i64(self.fetch_offset);
811        encoder.write_i32(self.max_bytes);
812        Ok(())
813    }
814}
815
816#[derive(Debug, Clone, PartialEq, Eq)]
817pub struct FetchResponseV2 {
818    pub throttle_time_ms: i32,
819    pub responses: Vec<FetchTopicResponseV2>,
820}
821
822#[derive(Debug, Clone, PartialEq, Eq)]
823pub struct FetchResponseV4 {
824    pub throttle_time_ms: i32,
825    pub responses: Vec<FetchTopicResponseV4>,
826}
827
828/// Fetch response version 11 with broker-selected read replicas.
829#[derive(Debug, Clone, PartialEq, Eq)]
830pub struct FetchResponseV11 {
831    pub throttle_time_ms: i32,
832    pub error_code: i16,
833    pub session_id: i32,
834    pub responses: Vec<FetchTopicResponseV11>,
835}
836
837/// Fetch response version 12 with flexible encoding and broker-selected reads.
838#[derive(Debug, Clone, PartialEq, Eq)]
839pub struct FetchResponseV12 {
840    pub throttle_time_ms: i32,
841    pub error_code: i16,
842    pub session_id: i32,
843    pub responses: Vec<FetchTopicResponseV12>,
844}
845
846/// Fetch response version 13 with topic UUIDs (KIP-516).
847#[derive(Debug, Clone, PartialEq, Eq)]
848pub struct FetchResponseV13 {
849    pub throttle_time_ms: i32,
850    pub error_code: i16,
851    pub session_id: i32,
852    pub responses: Vec<FetchTopicResponseV13>,
853}
854
855impl FetchResponseV12 {
856    pub fn decode_body(decoder: &mut Decoder<'_>) -> Result<Self> {
857        Ok(Self {
858            throttle_time_ms: decoder.read_i32()?,
859            error_code: decoder.read_i16()?,
860            session_id: decoder.read_i32()?,
861            responses: decoder
862                .read_compact_array("fetch responses", FetchTopicResponseV12::decode)?
863                .unwrap_or_default(),
864        })
865        .and_then(|response| {
866            decoder.read_tagged_fields()?;
867            Ok(response)
868        })
869    }
870}
871
872impl FetchResponseV13 {
873    pub fn decode_body(decoder: &mut Decoder<'_>) -> Result<Self> {
874        let response = Self {
875            throttle_time_ms: decoder.read_i32()?,
876            error_code: decoder.read_i16()?,
877            session_id: decoder.read_i32()?,
878            responses: decoder
879                .read_compact_array("fetch responses", FetchTopicResponseV13::decode)?
880                .unwrap_or_default(),
881        };
882        decoder.read_tagged_fields()?;
883        Ok(response)
884    }
885}
886
887#[derive(Debug, Clone, PartialEq, Eq)]
888pub struct FetchTopicResponseV13 {
889    pub topic_id: [u8; 16],
890    pub partitions: Vec<FetchPartitionResponseV13>,
891}
892
893impl FetchTopicResponseV13 {
894    fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
895        let topic_id = decoder.read_uuid()?;
896        let partitions = decoder
897            .read_compact_array(
898                "fetch partition responses",
899                FetchPartitionResponseV13::decode,
900            )?
901            .unwrap_or_default();
902        decoder.read_tagged_fields()?;
903        Ok(Self {
904            topic_id,
905            partitions,
906        })
907    }
908}
909
910pub type FetchPartitionResponseV13 = FetchPartitionResponseV12;
911pub type FetchResponseV14 = FetchResponseV13;
912pub type FetchTopicResponseV14 = FetchTopicResponseV13;
913pub type FetchPartitionResponseV14 = FetchPartitionResponseV13;
914pub type FetchForgottenTopicV14 = FetchForgottenTopicV13;
915pub type FetchPartitionResponseV16 = FetchPartitionResponseV13;
916pub type FetchTopicResponseV16 = FetchTopicResponseV13;
917pub type FetchResponseV15 = FetchResponseV13;
918pub type FetchTopicResponseV15 = FetchTopicResponseV13;
919pub type FetchPartitionResponseV15 = FetchPartitionResponseV13;
920pub type FetchForgottenTopicV15 = FetchForgottenTopicV13;
921
922/// A current-leader endpoint advertised by Fetch response v16 and newer.
923#[derive(Debug, Clone, PartialEq, Eq)]
924pub struct FetchNodeEndpointV16 {
925    pub node_id: i32,
926    pub host: String,
927    pub port: i32,
928    pub rack: Option<String>,
929}
930
931/// Fetch response version 16, including KIP-951 node endpoints.
932#[derive(Debug, Clone, PartialEq, Eq)]
933pub struct FetchResponseV16 {
934    pub throttle_time_ms: i32,
935    pub error_code: i16,
936    pub session_id: i32,
937    pub responses: Vec<FetchTopicResponseV16>,
938    pub node_endpoints: Vec<FetchNodeEndpointV16>,
939}
940
941impl FetchResponseV16 {
942    pub fn decode_body(decoder: &mut Decoder<'_>) -> Result<Self> {
943        let response = Self {
944            throttle_time_ms: decoder.read_i32()?,
945            error_code: decoder.read_i16()?,
946            session_id: decoder.read_i32()?,
947            responses: decoder
948                .read_compact_array("fetch responses", FetchTopicResponseV16::decode)?
949                .unwrap_or_default(),
950            node_endpoints: Vec::new(),
951        };
952        let tagged_fields = decoder.read_tagged_fields()?;
953        let node_endpoints = tagged_fields
954            .into_iter()
955            .find(|field| field.tag == 0)
956            .map(|field| decode_fetch_node_endpoints(&field.data, decoder.limits()))
957            .transpose()?
958            .unwrap_or_default();
959        Ok(Self {
960            node_endpoints,
961            ..response
962        })
963    }
964}
965
966fn decode_fetch_node_endpoints(
967    data: &[u8],
968    limits: DecodeLimits,
969) -> Result<Vec<FetchNodeEndpointV16>> {
970    let mut decoder = Decoder::with_limits(data, limits);
971    Ok(decoder
972        .read_compact_array("fetch node endpoints", |decoder| {
973            let endpoint = FetchNodeEndpointV16 {
974                node_id: decoder.read_i32()?,
975                host: decoder.read_compact_string()?,
976                port: decoder.read_i32()?,
977                rack: decoder.read_compact_nullable_string()?,
978            };
979            decoder.read_tagged_fields()?;
980            Ok(endpoint)
981        })?
982        .unwrap_or_default())
983}
984
985pub type FetchResponseV17 = FetchResponseV16;
986pub type FetchTopicResponseV17 = FetchTopicResponseV16;
987pub type FetchPartitionResponseV17 = FetchPartitionResponseV16;
988pub type FetchResponseV18 = FetchResponseV17;
989pub type FetchTopicResponseV18 = FetchTopicResponseV17;
990pub type FetchPartitionResponseV18 = FetchPartitionResponseV17;
991
992#[derive(Debug, Clone, PartialEq, Eq)]
993pub struct FetchTopicResponseV12 {
994    pub name: String,
995    pub partitions: Vec<FetchPartitionResponseV12>,
996}
997
998impl FetchTopicResponseV12 {
999    fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
1000        let name = decoder.read_compact_string()?;
1001        let partitions = decoder
1002            .read_compact_array(
1003                "fetch partition responses",
1004                FetchPartitionResponseV12::decode,
1005            )?
1006            .unwrap_or_default();
1007        decoder.read_tagged_fields()?;
1008        Ok(Self { name, partitions })
1009    }
1010}
1011
1012#[derive(Debug, Clone, PartialEq, Eq)]
1013pub struct FetchPartitionResponseV12 {
1014    pub partition_index: i32,
1015    pub error_code: i16,
1016    pub high_watermark: i64,
1017    pub last_stable_offset: i64,
1018    pub log_start_offset: i64,
1019    pub aborted_transactions: Vec<AbortedTransactionV12>,
1020    pub preferred_read_replica: i32,
1021    pub records: Vec<MessageSetRecord>,
1022}
1023
1024impl FetchPartitionResponseV12 {
1025    fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
1026        let limits = decoder.limits();
1027        let partition_index = decoder.read_i32()?;
1028        let error_code = decoder.read_i16()?;
1029        let high_watermark = decoder.read_i64()?;
1030        let last_stable_offset = decoder.read_i64()?;
1031        let log_start_offset = decoder.read_i64()?;
1032        let aborted_transactions = decoder
1033            .read_compact_array("aborted transactions", AbortedTransactionV12::decode)?
1034            .unwrap_or_default();
1035        let preferred_read_replica = decoder.read_i32()?;
1036        let records = decoder
1037            .read_compact_nullable_bytes()?
1038            .map(|bytes| decode_message_set(&bytes, limits))
1039            .transpose()?
1040            .unwrap_or_default();
1041        decoder.read_tagged_fields()?;
1042        Ok(Self {
1043            partition_index,
1044            error_code,
1045            high_watermark,
1046            last_stable_offset,
1047            log_start_offset,
1048            aborted_transactions,
1049            preferred_read_replica,
1050            records,
1051        })
1052    }
1053}
1054
1055#[derive(Debug, Clone, PartialEq, Eq)]
1056pub struct AbortedTransactionV12 {
1057    pub producer_id: i64,
1058    pub first_offset: i64,
1059}
1060
1061impl AbortedTransactionV12 {
1062    fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
1063        let producer_id = decoder.read_i64()?;
1064        let first_offset = decoder.read_i64()?;
1065        decoder.read_tagged_fields()?;
1066        Ok(Self {
1067            producer_id,
1068            first_offset,
1069        })
1070    }
1071}
1072
1073impl FetchResponseV11 {
1074    pub fn decode_body(decoder: &mut Decoder<'_>) -> Result<Self> {
1075        Ok(Self {
1076            throttle_time_ms: decoder.read_i32()?,
1077            error_code: decoder.read_i16()?,
1078            session_id: decoder.read_i32()?,
1079            responses: decoder
1080                .read_array("fetch responses", FetchTopicResponseV11::decode)?
1081                .unwrap_or_default(),
1082        })
1083    }
1084}
1085
1086#[derive(Debug, Clone, PartialEq, Eq)]
1087pub struct FetchTopicResponseV11 {
1088    pub name: String,
1089    pub partitions: Vec<FetchPartitionResponseV11>,
1090}
1091
1092impl FetchTopicResponseV11 {
1093    fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
1094        Ok(Self {
1095            name: decoder.read_string()?,
1096            partitions: decoder
1097                .read_array(
1098                    "fetch partition responses",
1099                    FetchPartitionResponseV11::decode,
1100                )?
1101                .unwrap_or_default(),
1102        })
1103    }
1104}
1105
1106#[derive(Debug, Clone, PartialEq, Eq)]
1107pub struct FetchPartitionResponseV11 {
1108    pub partition_index: i32,
1109    pub error_code: i16,
1110    pub high_watermark: i64,
1111    pub last_stable_offset: i64,
1112    pub log_start_offset: i64,
1113    pub aborted_transactions: Vec<AbortedTransactionV4>,
1114    pub preferred_read_replica: i32,
1115    pub records: Vec<MessageSetRecord>,
1116}
1117
1118impl FetchPartitionResponseV11 {
1119    fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
1120        let limits = decoder.limits();
1121        Ok(Self {
1122            partition_index: decoder.read_i32()?,
1123            error_code: decoder.read_i16()?,
1124            high_watermark: decoder.read_i64()?,
1125            last_stable_offset: decoder.read_i64()?,
1126            log_start_offset: decoder.read_i64()?,
1127            aborted_transactions: decoder
1128                .read_array("aborted transactions", AbortedTransactionV4::decode)?
1129                .unwrap_or_default(),
1130            preferred_read_replica: decoder.read_i32()?,
1131            records: decode_message_set(&decoder.read_bytes()?, limits)?,
1132        })
1133    }
1134}
1135
1136impl FetchResponseV4 {
1137    pub fn decode_body(decoder: &mut Decoder<'_>) -> Result<Self> {
1138        Ok(Self {
1139            throttle_time_ms: decoder.read_i32()?,
1140            responses: decoder
1141                .read_array("fetch responses", FetchTopicResponseV4::decode)?
1142                .unwrap_or_default(),
1143        })
1144    }
1145}
1146
1147#[derive(Debug, Clone, PartialEq, Eq)]
1148pub struct FetchTopicResponseV4 {
1149    pub name: String,
1150    pub partitions: Vec<FetchPartitionResponseV4>,
1151}
1152
1153impl FetchTopicResponseV4 {
1154    fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
1155        Ok(Self {
1156            name: decoder.read_string()?,
1157            partitions: decoder
1158                .read_array(
1159                    "fetch partition responses",
1160                    FetchPartitionResponseV4::decode,
1161                )?
1162                .unwrap_or_default(),
1163        })
1164    }
1165}
1166
1167#[derive(Debug, Clone, PartialEq, Eq)]
1168pub struct FetchPartitionResponseV4 {
1169    pub partition_index: i32,
1170    pub error_code: i16,
1171    pub high_watermark: i64,
1172    pub last_stable_offset: i64,
1173    pub aborted_transactions: Vec<AbortedTransactionV4>,
1174    pub records: Vec<MessageSetRecord>,
1175}
1176
1177impl FetchPartitionResponseV4 {
1178    fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
1179        let limits = decoder.limits();
1180        Ok(Self {
1181            partition_index: decoder.read_i32()?,
1182            error_code: decoder.read_i16()?,
1183            high_watermark: decoder.read_i64()?,
1184            last_stable_offset: decoder.read_i64()?,
1185            aborted_transactions: decoder
1186                .read_array("aborted transactions", AbortedTransactionV4::decode)?
1187                .unwrap_or_default(),
1188            records: decode_message_set(&decoder.read_bytes()?, limits)?,
1189        })
1190    }
1191}
1192
1193#[derive(Debug, Clone, PartialEq, Eq)]
1194pub struct AbortedTransactionV4 {
1195    pub producer_id: i64,
1196    pub first_offset: i64,
1197}
1198
1199impl AbortedTransactionV4 {
1200    fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
1201        Ok(Self {
1202            producer_id: decoder.read_i64()?,
1203            first_offset: decoder.read_i64()?,
1204        })
1205    }
1206}
1207
1208impl FetchResponseV2 {
1209    pub fn decode_body(decoder: &mut Decoder<'_>) -> Result<Self> {
1210        Ok(Self {
1211            throttle_time_ms: decoder.read_i32()?,
1212            responses: decoder
1213                .read_array("fetch responses", FetchTopicResponseV2::decode)?
1214                .unwrap_or_default(),
1215        })
1216    }
1217}
1218
1219#[derive(Debug, Clone, PartialEq, Eq)]
1220pub struct FetchTopicResponseV2 {
1221    pub name: String,
1222    pub partitions: Vec<FetchPartitionResponseV2>,
1223}
1224
1225impl FetchTopicResponseV2 {
1226    fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
1227        Ok(Self {
1228            name: decoder.read_string()?,
1229            partitions: decoder
1230                .read_array(
1231                    "fetch partition responses",
1232                    FetchPartitionResponseV2::decode,
1233                )?
1234                .unwrap_or_default(),
1235        })
1236    }
1237}
1238
1239#[derive(Debug, Clone, PartialEq, Eq)]
1240pub struct FetchPartitionResponseV2 {
1241    pub partition_index: i32,
1242    pub error_code: i16,
1243    pub high_watermark: i64,
1244    pub records: Vec<MessageSetRecord>,
1245}
1246
1247impl FetchPartitionResponseV2 {
1248    fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
1249        let limits = decoder.limits();
1250        Ok(Self {
1251            partition_index: decoder.read_i32()?,
1252            error_code: decoder.read_i16()?,
1253            high_watermark: decoder.read_i64()?,
1254            records: decode_message_set(&decoder.read_bytes()?, limits)?,
1255        })
1256    }
1257}
1258
1259#[derive(Debug, Clone, PartialEq, Eq)]
1260pub struct MessageSetRecord {
1261    pub offset: i64,
1262    /// Kafka's partition leader epoch for the enclosing RecordBatch.
1263    /// Legacy MessageSet records do not carry an epoch and use `-1`.
1264    pub leader_epoch: i32,
1265    pub timestamp_ms: i64,
1266    pub key: Option<Vec<u8>>,
1267    pub value: Option<Vec<u8>>,
1268    pub headers: Vec<RecordBatchHeader>,
1269    pub producer_id: Option<i64>,
1270    pub transactional: bool,
1271    pub control: bool,
1272}
1273
1274/// Decodes Kafka MessageSet or RecordBatch bytes returned by a fetch-family
1275/// response.
1276///
1277/// ShareFetch uses the same record-batch encoding as Fetch, so the high-level
1278/// share consumer can reuse the exact compression, header, transactional, and
1279/// resource-limit handling implemented here.
1280pub fn decode_message_set(bytes: &[u8], limits: DecodeLimits) -> Result<Vec<MessageSetRecord>> {
1281    let mut decoder = Decoder::with_limits(bytes, limits);
1282    let mut records = Vec::new();
1283
1284    while decoder.remaining() >= 12 {
1285        let offset = decoder.read_i64()?;
1286        let message_size = decoder.read_i32()?;
1287        if message_size < 0 {
1288            return Err(Error::NegativeLength {
1289                kind: "message",
1290                length: message_size,
1291            });
1292        }
1293        let message_size =
1294            usize::try_from(message_size).map_err(|_| Error::LengthOverflow("message"))?;
1295        // Fetch responses may end with a partial trailing message set entry.
1296        if decoder.remaining() < message_size {
1297            break;
1298        }
1299        let message = decoder.read_exact(message_size)?;
1300        let decoded = decode_message_or_batch(offset, message, limits)?;
1301        let total = records
1302            .len()
1303            .checked_add(decoded.len())
1304            .ok_or(Error::LengthOverflow("fetch records"))?;
1305        decoder.ensure_collection_length("fetch records", total)?;
1306        records.extend(decoded);
1307    }
1308
1309    Ok(records)
1310}
1311
1312fn decode_message_or_batch(
1313    offset: i64,
1314    bytes: &[u8],
1315    limits: DecodeLimits,
1316) -> Result<Vec<MessageSetRecord>> {
1317    match bytes.get(4).copied() {
1318        Some(2) => decode_record_batch(offset, bytes, limits),
1319        _ => Ok(vec![decode_message(offset, bytes, limits)?]),
1320    }
1321}
1322
1323fn decode_message(offset: i64, bytes: &[u8], limits: DecodeLimits) -> Result<MessageSetRecord> {
1324    let mut decoder = Decoder::with_limits(bytes, limits);
1325    let _crc = decoder.read_i32()?;
1326    let magic = decoder.read_i8()?;
1327    let _attributes = decoder.read_i8()?;
1328    let timestamp_ms = match magic {
1329        0 => -1,
1330        1 => decoder.read_i64()?,
1331        _ => {
1332            return Err(Error::UnsupportedVersion {
1333                kind: "message magic",
1334                version: i16::from(magic),
1335            })
1336        }
1337    };
1338    let key = decoder.read_nullable_bytes()?;
1339    let value = decoder.read_nullable_bytes()?;
1340
1341    Ok(MessageSetRecord {
1342        offset,
1343        leader_epoch: -1,
1344        timestamp_ms,
1345        key,
1346        value,
1347        headers: Vec::new(),
1348        producer_id: None,
1349        transactional: false,
1350        control: false,
1351    })
1352}
1353
1354fn decode_record_batch(
1355    base_offset: i64,
1356    bytes: &[u8],
1357    limits: DecodeLimits,
1358) -> Result<Vec<MessageSetRecord>> {
1359    let mut decoder = Decoder::with_limits(bytes, limits);
1360    let partition_leader_epoch = decoder.read_i32()?;
1361    let magic = decoder.read_i8()?;
1362    if magic != 2 {
1363        return Err(Error::UnsupportedVersion {
1364            kind: "record batch magic",
1365            version: i16::from(magic),
1366        });
1367    }
1368    let _crc = decoder.read_i32()?;
1369    let attributes = decoder.read_i16()?;
1370    let compression = RecordBatchCompression::from_attributes(attributes)?;
1371    let _last_offset_delta = decoder.read_i32()?;
1372    let base_timestamp = decoder.read_i64()?;
1373    let _max_timestamp = decoder.read_i64()?;
1374    let producer_id = decoder.read_i64()?;
1375    let _producer_epoch = decoder.read_i16()?;
1376    let _base_sequence = decoder.read_i32()?;
1377    let record_count = decoder.read_i32()?;
1378    if record_count < 0 {
1379        return Err(Error::NegativeLength {
1380            kind: "record batch records",
1381            length: record_count,
1382        });
1383    }
1384
1385    let record_count =
1386        usize::try_from(record_count).map_err(|_| Error::LengthOverflow("record batch records"))?;
1387    decoder.ensure_collection_length("record batch records", record_count)?;
1388    let record_bytes = if compression.is_compressed() {
1389        let compressed = decoder.read_exact(decoder.remaining())?;
1390        decompress_record_batch_records_with_limit(
1391            compression,
1392            compressed,
1393            limits.max_decompressed_record_bytes(),
1394        )?
1395    } else {
1396        if decoder.remaining() > limits.max_decompressed_record_bytes() {
1397            return Err(Error::LimitExceeded {
1398                kind: "decompressed record batch bytes",
1399                actual: decoder.remaining(),
1400                max: limits.max_decompressed_record_bytes(),
1401            });
1402        }
1403        decoder.read_exact(decoder.remaining())?.to_vec()
1404    };
1405    let mut record_decoder = Decoder::with_limits(&record_bytes, limits);
1406    let mut records = Vec::with_capacity(record_count);
1407    for _ in 0..record_count {
1408        let record_length = record_decoder.read_varint()?;
1409        if record_length < 0 {
1410            return Err(Error::NegativeLength {
1411                kind: "record",
1412                length: record_length,
1413            });
1414        }
1415        let record_length =
1416            usize::try_from(record_length).map_err(|_| Error::LengthOverflow("record"))?;
1417        let record_bytes = record_decoder.read_exact(record_length)?;
1418        records.push(decode_record(
1419            base_offset,
1420            partition_leader_epoch,
1421            base_timestamp,
1422            producer_id,
1423            attributes,
1424            record_bytes,
1425            limits,
1426        )?);
1427    }
1428
1429    Ok(records)
1430}
1431
1432fn decode_record(
1433    base_offset: i64,
1434    partition_leader_epoch: i32,
1435    base_timestamp: i64,
1436    producer_id: i64,
1437    batch_attributes: i16,
1438    bytes: &[u8],
1439    limits: DecodeLimits,
1440) -> Result<MessageSetRecord> {
1441    let mut decoder = Decoder::with_limits(bytes, limits);
1442    let _attributes = decoder.read_i8()?;
1443    let timestamp_delta = decoder.read_varlong()?;
1444    let offset_delta = decoder.read_varint()?;
1445    let key = decoder.read_varint_nullable_bytes()?;
1446    let value = decoder.read_varint_nullable_bytes()?;
1447    let header_count = decoder.read_varint()?;
1448    if header_count < 0 {
1449        return Err(Error::NegativeLength {
1450            kind: "record headers",
1451            length: header_count,
1452        });
1453    }
1454    let header_count =
1455        usize::try_from(header_count).map_err(|_| Error::LengthOverflow("record headers"))?;
1456    decoder.ensure_collection_length("record headers", header_count)?;
1457    let mut headers = Vec::with_capacity(header_count);
1458    for _ in 0..header_count {
1459        let header_key =
1460            String::from_utf8(decoder.read_varint_bytes()?).map_err(|_| Error::InvalidUtf8)?;
1461        let header_value = decoder.read_varint_nullable_bytes()?;
1462        headers.push(RecordBatchHeader::new(header_key, header_value));
1463    }
1464
1465    Ok(MessageSetRecord {
1466        offset: base_offset.saturating_add(i64::from(offset_delta)),
1467        leader_epoch: partition_leader_epoch,
1468        timestamp_ms: base_timestamp.saturating_add(timestamp_delta),
1469        key,
1470        value,
1471        headers,
1472        producer_id: (producer_id >= 0).then_some(producer_id),
1473        transactional: batch_attributes & 0x10 != 0,
1474        control: batch_attributes & 0x20 != 0,
1475    })
1476}
1477
1478#[cfg(test)]
1479#[allow(clippy::unwrap_used)]
1480mod tests {
1481    use super::{
1482        FetchPartitionV11, FetchPartitionV12, FetchPartitionV17, FetchPartitionV18,
1483        FetchPartitionV2, FetchReplicaStateV15, FetchRequestV11, FetchRequestV12, FetchRequestV13,
1484        FetchRequestV14, FetchRequestV15, FetchRequestV16, FetchRequestV17, FetchRequestV18,
1485        FetchRequestV2, FetchRequestV4, FetchResponseV11, FetchResponseV12, FetchResponseV13,
1486        FetchResponseV16, FetchResponseV2, FetchResponseV4, FetchTopicV11, FetchTopicV12,
1487        FetchTopicV13, FetchTopicV17, FetchTopicV18, FetchTopicV2, MessageSetRecord,
1488        RecordBatchHeader,
1489    };
1490    use crate::codec::{Decoder, Encoder};
1491
1492    #[test]
1493    fn encodes_fetch_request_v2() {
1494        let request = FetchRequestV2 {
1495            correlation_id: 7,
1496            client_id: Some("kafrust".to_owned()),
1497            replica_id: -1,
1498            max_wait_ms: 500,
1499            min_bytes: 1,
1500            topics: vec![FetchTopicV2 {
1501                name: "orders".to_owned(),
1502                partitions: vec![FetchPartitionV2 {
1503                    partition_index: 0,
1504                    fetch_offset: 42,
1505                    max_bytes: 1_048_576,
1506                }],
1507            }],
1508        };
1509
1510        let bytes = request.encode().unwrap();
1511        assert_eq!(&bytes[0..4], &[0, 1, 0, 2]);
1512        assert!(bytes.len() > 40);
1513    }
1514
1515    #[test]
1516    fn encodes_fetch_request_v4() {
1517        let request = FetchRequestV4 {
1518            correlation_id: 8,
1519            client_id: Some("kafrust".to_owned()),
1520            replica_id: -1,
1521            max_wait_ms: 500,
1522            min_bytes: 1,
1523            max_bytes: 1_048_576,
1524            isolation_level: 0,
1525            topics: vec![FetchTopicV2 {
1526                name: "orders".to_owned(),
1527                partitions: vec![FetchPartitionV2 {
1528                    partition_index: 0,
1529                    fetch_offset: 42,
1530                    max_bytes: 1_048_576,
1531                }],
1532            }],
1533        };
1534
1535        let bytes = request.encode().unwrap();
1536        assert_eq!(&bytes[0..4], &[0, 1, 0, 4]);
1537        assert_eq!(&bytes[4..8], &[0, 0, 0, 8]);
1538        assert!(bytes.len() > 45);
1539    }
1540
1541    #[test]
1542    fn encodes_fetch_request_v11_with_rack_and_fetch_session_fields() {
1543        let request = FetchRequestV11 {
1544            correlation_id: 9,
1545            client_id: Some("kafrust".to_owned()),
1546            replica_id: -1,
1547            max_wait_ms: 500,
1548            min_bytes: 1,
1549            max_bytes: 1_048_576,
1550            isolation_level: 1,
1551            session_id: 0,
1552            session_epoch: 0,
1553            topics: vec![FetchTopicV11 {
1554                name: "orders".to_owned(),
1555                partitions: vec![FetchPartitionV11 {
1556                    partition_index: 0,
1557                    current_leader_epoch: -1,
1558                    fetch_offset: 42,
1559                    log_start_offset: -1,
1560                    max_bytes: 1_048_576,
1561                }],
1562            }],
1563            forgotten_topics: Vec::new(),
1564            rack_id: "rack-a".to_owned(),
1565        };
1566
1567        let bytes = request.encode().unwrap();
1568        let mut decoder = Decoder::new(&bytes);
1569        assert_eq!(decoder.read_i16().unwrap(), 1);
1570        assert_eq!(decoder.read_i16().unwrap(), 11);
1571        assert_eq!(decoder.read_i32().unwrap(), 9);
1572        assert_eq!(
1573            decoder.read_nullable_string().unwrap().as_deref(),
1574            Some("kafrust")
1575        );
1576        assert_eq!(decoder.read_i32().unwrap(), -1);
1577        assert_eq!(decoder.read_i32().unwrap(), 500);
1578        assert_eq!(decoder.read_i32().unwrap(), 1);
1579        assert_eq!(decoder.read_i32().unwrap(), 1_048_576);
1580        assert_eq!(decoder.read_i8().unwrap(), 1);
1581        assert_eq!(decoder.read_i32().unwrap(), 0);
1582        assert_eq!(decoder.read_i32().unwrap(), 0);
1583        assert_eq!(decoder.read_i32().unwrap(), 1);
1584        assert_eq!(decoder.read_string().unwrap(), "orders");
1585        assert_eq!(decoder.read_i32().unwrap(), 1);
1586        assert_eq!(decoder.read_i32().unwrap(), 0);
1587        assert_eq!(decoder.read_i32().unwrap(), -1);
1588        assert_eq!(decoder.read_i64().unwrap(), 42);
1589        assert_eq!(decoder.read_i64().unwrap(), -1);
1590        assert_eq!(decoder.read_i32().unwrap(), 1_048_576);
1591        assert_eq!(decoder.read_i32().unwrap(), 0);
1592        assert_eq!(decoder.read_string().unwrap(), "rack-a");
1593        assert!(decoder.is_empty());
1594    }
1595
1596    #[test]
1597    fn decodes_fetch_response_v11_with_preferred_read_replica() {
1598        let mut bytes = Encoder::new();
1599        bytes.write_i32(3);
1600        bytes.write_i16(0);
1601        bytes.write_i32(17);
1602        bytes.write_i32(1);
1603        bytes.write_string("orders").unwrap();
1604        bytes.write_i32(1);
1605        bytes.write_i32(0);
1606        bytes.write_i16(0);
1607        bytes.write_i64(43);
1608        bytes.write_i64(42);
1609        bytes.write_i64(40);
1610        bytes.write_i32(0);
1611        bytes.write_i32(2);
1612        bytes.write_bytes(&[]).unwrap();
1613
1614        let bytes = bytes.into_bytes();
1615        let mut decoder = Decoder::new(&bytes);
1616        let response = FetchResponseV11::decode_body(&mut decoder).unwrap();
1617        let partition = &response.responses[0].partitions[0];
1618
1619        assert_eq!(response.throttle_time_ms, 3);
1620        assert_eq!(response.session_id, 17);
1621        assert_eq!(partition.log_start_offset, 40);
1622        assert_eq!(partition.preferred_read_replica, 2);
1623        assert!(partition.records.is_empty());
1624        assert!(decoder.is_empty());
1625    }
1626
1627    #[test]
1628    fn encodes_fetch_request_v12_with_flexible_rack_fields() {
1629        let request = FetchRequestV12 {
1630            correlation_id: 10,
1631            client_id: Some("kafrust".to_owned()),
1632            replica_id: -1,
1633            max_wait_ms: 500,
1634            min_bytes: 1,
1635            max_bytes: 1_048_576,
1636            isolation_level: 1,
1637            session_id: 0,
1638            session_epoch: 0,
1639            topics: vec![FetchTopicV12 {
1640                name: "orders".to_owned(),
1641                partitions: vec![FetchPartitionV12 {
1642                    partition_index: 0,
1643                    current_leader_epoch: -1,
1644                    fetch_offset: 42,
1645                    last_fetched_epoch: -1,
1646                    log_start_offset: -1,
1647                    max_bytes: 1_048_576,
1648                }],
1649            }],
1650            forgotten_topics: Vec::new(),
1651            rack_id: "rack-a".to_owned(),
1652        };
1653
1654        let bytes = request.encode().unwrap();
1655        let mut decoder = Decoder::new(&bytes);
1656        assert_eq!(decoder.read_i16().unwrap(), 1);
1657        assert_eq!(decoder.read_i16().unwrap(), 12);
1658        assert_eq!(decoder.read_i32().unwrap(), 10);
1659        assert_eq!(
1660            decoder.read_nullable_string().unwrap().as_deref(),
1661            Some("kafrust")
1662        );
1663        assert_eq!(decoder.read_unsigned_varint().unwrap(), 0);
1664        assert_eq!(decoder.read_i32().unwrap(), -1);
1665        assert_eq!(decoder.read_i32().unwrap(), 500);
1666        assert_eq!(decoder.read_i32().unwrap(), 1);
1667        assert_eq!(decoder.read_i32().unwrap(), 1_048_576);
1668        assert_eq!(decoder.read_i8().unwrap(), 1);
1669        assert_eq!(decoder.read_i32().unwrap(), 0);
1670        assert_eq!(decoder.read_i32().unwrap(), 0);
1671        assert_eq!(decoder.read_unsigned_varint().unwrap(), 2);
1672        assert_eq!(decoder.read_compact_string().unwrap(), "orders");
1673        assert_eq!(decoder.read_unsigned_varint().unwrap(), 2);
1674        assert_eq!(decoder.read_i32().unwrap(), 0);
1675        assert_eq!(decoder.read_i32().unwrap(), -1);
1676        assert_eq!(decoder.read_i64().unwrap(), 42);
1677        assert_eq!(decoder.read_i32().unwrap(), -1);
1678        assert_eq!(decoder.read_i64().unwrap(), -1);
1679        assert_eq!(decoder.read_i32().unwrap(), 1_048_576);
1680        assert_eq!(decoder.read_unsigned_varint().unwrap(), 0);
1681        assert_eq!(decoder.read_unsigned_varint().unwrap(), 0);
1682        assert_eq!(decoder.read_unsigned_varint().unwrap(), 1);
1683        assert_eq!(decoder.read_compact_string().unwrap(), "rack-a");
1684        assert_eq!(decoder.read_unsigned_varint().unwrap(), 0);
1685        assert!(decoder.is_empty());
1686    }
1687
1688    #[test]
1689    fn decodes_fetch_response_v12_with_preferred_read_replica() {
1690        let mut bytes = Encoder::new();
1691        bytes.write_i32(3);
1692        bytes.write_i16(0);
1693        bytes.write_i32(17);
1694        bytes.write_unsigned_varint(2);
1695        bytes.write_compact_string("orders").unwrap();
1696        bytes.write_unsigned_varint(2);
1697        bytes.write_i32(0);
1698        bytes.write_i16(0);
1699        bytes.write_i64(43);
1700        bytes.write_i64(42);
1701        bytes.write_i64(40);
1702        bytes.write_unsigned_varint(2);
1703        bytes.write_i64(7);
1704        bytes.write_i64(40);
1705        bytes.write_unsigned_varint(0);
1706        bytes.write_i32(2);
1707        bytes.write_compact_nullable_bytes(Some(&[])).unwrap();
1708        bytes.write_unsigned_varint(0);
1709        bytes.write_unsigned_varint(0);
1710        bytes.write_unsigned_varint(0);
1711
1712        let bytes = bytes.into_bytes();
1713        let mut decoder = Decoder::new(&bytes);
1714        let response = FetchResponseV12::decode_body(&mut decoder).unwrap();
1715        let partition = &response.responses[0].partitions[0];
1716
1717        assert_eq!(response.throttle_time_ms, 3);
1718        assert_eq!(response.session_id, 17);
1719        assert_eq!(partition.log_start_offset, 40);
1720        assert_eq!(partition.preferred_read_replica, 2);
1721        assert_eq!(partition.aborted_transactions[0].producer_id, 7);
1722        assert!(partition.records.is_empty());
1723        assert!(decoder.is_empty());
1724    }
1725
1726    #[test]
1727    fn encodes_fetch_request_v13_with_topic_uuid_and_cluster_tag() {
1728        let request = FetchRequestV13 {
1729            correlation_id: 11,
1730            client_id: Some("kafrust".to_owned()),
1731            cluster_id: Some("cluster-a".to_owned()),
1732            replica_id: -1,
1733            max_wait_ms: 500,
1734            min_bytes: 1,
1735            max_bytes: 1_048_576,
1736            isolation_level: 1,
1737            session_id: 17,
1738            session_epoch: 2,
1739            topics: vec![FetchTopicV13 {
1740                topic_id: [3; 16],
1741                partitions: vec![FetchPartitionV12 {
1742                    partition_index: 0,
1743                    current_leader_epoch: 4,
1744                    fetch_offset: 42,
1745                    last_fetched_epoch: 3,
1746                    log_start_offset: -1,
1747                    max_bytes: 1_048_576,
1748                }],
1749            }],
1750            forgotten_topics: Vec::new(),
1751            rack_id: "rack-a".to_owned(),
1752        };
1753
1754        let bytes = request.encode().unwrap();
1755        let mut decoder = Decoder::new(&bytes);
1756        assert_eq!(decoder.read_i16().unwrap(), 1);
1757        assert_eq!(decoder.read_i16().unwrap(), 13);
1758        assert_eq!(decoder.read_i32().unwrap(), 11);
1759        assert_eq!(
1760            decoder.read_nullable_string().unwrap().as_deref(),
1761            Some("kafrust")
1762        );
1763        assert!(decoder.read_tagged_fields().unwrap().is_empty());
1764        assert_eq!(decoder.read_i32().unwrap(), -1);
1765        assert_eq!(decoder.read_i32().unwrap(), 500);
1766        assert_eq!(decoder.read_i32().unwrap(), 1);
1767        assert_eq!(decoder.read_i32().unwrap(), 1_048_576);
1768        assert_eq!(decoder.read_i8().unwrap(), 1);
1769        assert_eq!(decoder.read_i32().unwrap(), 17);
1770        assert_eq!(decoder.read_i32().unwrap(), 2);
1771        assert_eq!(decoder.read_unsigned_varint().unwrap(), 2);
1772        assert_eq!(decoder.read_uuid().unwrap(), [3; 16]);
1773        assert_eq!(decoder.read_unsigned_varint().unwrap(), 2);
1774        assert_eq!(decoder.read_i32().unwrap(), 0);
1775        assert_eq!(decoder.read_i32().unwrap(), 4);
1776        assert_eq!(decoder.read_i64().unwrap(), 42);
1777        assert_eq!(decoder.read_i32().unwrap(), 3);
1778        assert_eq!(decoder.read_i64().unwrap(), -1);
1779        assert_eq!(decoder.read_i32().unwrap(), 1_048_576);
1780        assert!(decoder.read_tagged_fields().unwrap().is_empty());
1781        assert!(decoder.read_tagged_fields().unwrap().is_empty());
1782        assert_eq!(decoder.read_unsigned_varint().unwrap(), 1);
1783        assert_eq!(decoder.read_compact_string().unwrap(), "rack-a");
1784        let fields = decoder.read_tagged_fields().unwrap();
1785        assert_eq!(fields.len(), 1);
1786        let mut cluster_decoder = Decoder::new(&fields[0].data);
1787        assert_eq!(fields[0].tag, 0);
1788        assert_eq!(cluster_decoder.read_compact_string().unwrap(), "cluster-a");
1789        assert!(cluster_decoder.is_empty());
1790        assert!(decoder.is_empty());
1791    }
1792
1793    #[test]
1794    fn decodes_fetch_response_v13_with_topic_uuid() {
1795        let mut bytes = Encoder::new();
1796        bytes.write_i32(3);
1797        bytes.write_i16(0);
1798        bytes.write_i32(17);
1799        bytes.write_unsigned_varint(2);
1800        bytes.write_uuid(&[4; 16]);
1801        bytes.write_unsigned_varint(2);
1802        bytes.write_i32(0);
1803        bytes.write_i16(0);
1804        bytes.write_i64(43);
1805        bytes.write_i64(42);
1806        bytes.write_i64(40);
1807        bytes.write_unsigned_varint(1);
1808        bytes.write_i32(2);
1809        bytes.write_compact_nullable_bytes(Some(&[])).unwrap();
1810        bytes.write_empty_tagged_fields();
1811        bytes.write_empty_tagged_fields();
1812        bytes.write_empty_tagged_fields();
1813
1814        let bytes = bytes.into_bytes();
1815        let mut decoder = Decoder::new(&bytes);
1816        let response = FetchResponseV13::decode_body(&mut decoder).unwrap();
1817        let partition = &response.responses[0].partitions[0];
1818
1819        assert_eq!(response.session_id, 17);
1820        assert_eq!(response.responses[0].topic_id, [4; 16]);
1821        assert_eq!(partition.high_watermark, 43);
1822        assert_eq!(partition.preferred_read_replica, 2);
1823        assert!(partition.records.is_empty());
1824        assert!(decoder.is_empty());
1825    }
1826
1827    #[test]
1828    fn decodes_fetch_response_v16_node_endpoints_tag() {
1829        let mut endpoint = Encoder::new();
1830        endpoint.write_unsigned_varint(2);
1831        endpoint.write_i32(3);
1832        endpoint.write_compact_string("broker-a").unwrap();
1833        endpoint.write_i32(9092);
1834        endpoint
1835            .write_compact_nullable_string(Some("rack-a"))
1836            .unwrap();
1837        endpoint.write_empty_tagged_fields();
1838        let endpoint = endpoint.into_bytes();
1839
1840        let mut bytes = Encoder::new();
1841        bytes.write_i32(3);
1842        bytes.write_i16(0);
1843        bytes.write_i32(17);
1844        bytes.write_unsigned_varint(1);
1845        bytes.write_unsigned_varint(1);
1846        bytes.write_unsigned_varint(0);
1847        bytes.write_unsigned_varint(endpoint.len() as u32);
1848        bytes.write_raw(&endpoint);
1849
1850        let bytes = bytes.into_bytes();
1851        let mut decoder = Decoder::new(&bytes);
1852        let response = FetchResponseV16::decode_body(&mut decoder).unwrap();
1853        assert_eq!(response.node_endpoints.len(), 1);
1854        assert_eq!(response.node_endpoints[0].node_id, 3);
1855        assert_eq!(response.node_endpoints[0].host, "broker-a");
1856        assert_eq!(response.node_endpoints[0].port, 9092);
1857        assert_eq!(response.node_endpoints[0].rack.as_deref(), Some("rack-a"));
1858        assert!(decoder.is_empty());
1859    }
1860
1861    #[test]
1862    fn encodes_fetch_request_v14_with_the_v14_header_version() {
1863        let request = FetchRequestV14 {
1864            correlation_id: 12,
1865            client_id: None,
1866            cluster_id: None,
1867            replica_id: -1,
1868            max_wait_ms: 500,
1869            min_bytes: 1,
1870            max_bytes: 1_048_576,
1871            isolation_level: 0,
1872            session_id: 0,
1873            session_epoch: -1,
1874            topics: Vec::new(),
1875            forgotten_topics: Vec::new(),
1876            rack_id: String::new(),
1877        };
1878
1879        let bytes = request.encode().unwrap();
1880        assert_eq!(&bytes[0..4], &[0, 1, 0, 14]);
1881        let mut decoder = Decoder::new(&bytes);
1882        decoder.read_i16().unwrap();
1883        decoder.read_i16().unwrap();
1884        decoder.read_i32().unwrap();
1885        decoder.read_nullable_string().unwrap();
1886        assert!(decoder.read_tagged_fields().unwrap().is_empty());
1887        assert_eq!(decoder.read_i32().unwrap(), -1);
1888        assert_eq!(decoder.read_i32().unwrap(), 500);
1889        assert_eq!(decoder.read_i32().unwrap(), 1);
1890        assert_eq!(decoder.read_i32().unwrap(), 1_048_576);
1891        assert_eq!(decoder.read_i8().unwrap(), 0);
1892        assert_eq!(decoder.read_i32().unwrap(), 0);
1893        assert_eq!(decoder.read_i32().unwrap(), -1);
1894        assert_eq!(decoder.read_unsigned_varint().unwrap(), 1);
1895        assert_eq!(decoder.read_unsigned_varint().unwrap(), 1);
1896        assert!(decoder.read_compact_string().unwrap().is_empty());
1897        assert!(decoder.read_tagged_fields().unwrap().is_empty());
1898        assert!(decoder.is_empty());
1899    }
1900
1901    #[test]
1902    fn encodes_fetch_request_v15_replica_state_as_tagged_struct() {
1903        let request = FetchRequestV15 {
1904            correlation_id: 13,
1905            client_id: Some("kafrust".to_owned()),
1906            cluster_id: Some("cluster-a".to_owned()),
1907            replica_state: Some(FetchReplicaStateV15 {
1908                replica_id: 4,
1909                replica_epoch: 9,
1910            }),
1911            max_wait_ms: 500,
1912            min_bytes: 1,
1913            max_bytes: 1_048_576,
1914            isolation_level: 0,
1915            session_id: 0,
1916            session_epoch: -1,
1917            topics: Vec::new(),
1918            forgotten_topics: Vec::new(),
1919            rack_id: String::new(),
1920        };
1921
1922        let bytes = request.encode().unwrap();
1923        let mut decoder = Decoder::new(&bytes);
1924        assert_eq!(decoder.read_i16().unwrap(), 1);
1925        assert_eq!(decoder.read_i16().unwrap(), 15);
1926        assert_eq!(decoder.read_i32().unwrap(), 13);
1927        assert_eq!(
1928            decoder.read_nullable_string().unwrap().as_deref(),
1929            Some("kafrust")
1930        );
1931        assert!(decoder.read_tagged_fields().unwrap().is_empty());
1932        assert_eq!(decoder.read_i32().unwrap(), 500);
1933        assert_eq!(decoder.read_i32().unwrap(), 1);
1934        assert_eq!(decoder.read_i32().unwrap(), 1_048_576);
1935        assert_eq!(decoder.read_i8().unwrap(), 0);
1936        assert_eq!(decoder.read_i32().unwrap(), 0);
1937        assert_eq!(decoder.read_i32().unwrap(), -1);
1938        assert_eq!(decoder.read_unsigned_varint().unwrap(), 1);
1939        assert_eq!(decoder.read_unsigned_varint().unwrap(), 1);
1940        assert!(decoder.read_compact_string().unwrap().is_empty());
1941        let fields = decoder.read_tagged_fields().unwrap();
1942        assert_eq!(fields.len(), 2);
1943        assert_eq!(fields[0].tag, 0);
1944        assert_eq!(fields[1].tag, 1);
1945        let mut cluster_decoder = Decoder::new(&fields[0].data);
1946        assert_eq!(cluster_decoder.read_compact_string().unwrap(), "cluster-a");
1947        let mut replica_decoder = Decoder::new(&fields[1].data);
1948        assert_eq!(replica_decoder.read_i32().unwrap(), 4);
1949        assert_eq!(replica_decoder.read_i64().unwrap(), 9);
1950        assert!(decoder.is_empty());
1951    }
1952
1953    #[test]
1954    fn encodes_fetch_request_v16_with_the_v16_header_version() {
1955        let request = FetchRequestV16 {
1956            correlation_id: 14,
1957            client_id: None,
1958            cluster_id: None,
1959            replica_state: None,
1960            max_wait_ms: 500,
1961            min_bytes: 1,
1962            max_bytes: 1_048_576,
1963            isolation_level: 0,
1964            session_id: 0,
1965            session_epoch: -1,
1966            topics: Vec::new(),
1967            forgotten_topics: Vec::new(),
1968            rack_id: String::new(),
1969        };
1970
1971        let bytes = request.encode().unwrap();
1972        assert_eq!(&bytes[0..4], &[0, 1, 0, 16]);
1973    }
1974
1975    #[test]
1976    fn encodes_fetch_request_v17_directory_id_as_partition_tag() {
1977        let request = FetchRequestV17 {
1978            correlation_id: 15,
1979            client_id: None,
1980            cluster_id: None,
1981            replica_state: None,
1982            max_wait_ms: 500,
1983            min_bytes: 1,
1984            max_bytes: 1_048_576,
1985            isolation_level: 0,
1986            session_id: 0,
1987            session_epoch: -1,
1988            topics: vec![FetchTopicV17 {
1989                topic_id: [7; 16],
1990                partitions: vec![FetchPartitionV17 {
1991                    partition_index: 0,
1992                    current_leader_epoch: -1,
1993                    fetch_offset: 42,
1994                    last_fetched_epoch: -1,
1995                    log_start_offset: -1,
1996                    max_bytes: 1_048_576,
1997                    replica_directory_id: Some([8; 16]),
1998                }],
1999            }],
2000            forgotten_topics: Vec::new(),
2001            rack_id: String::new(),
2002        };
2003
2004        let bytes = request.encode().unwrap();
2005        let mut decoder = Decoder::new(&bytes);
2006        assert_eq!(decoder.read_i16().unwrap(), 1);
2007        assert_eq!(decoder.read_i16().unwrap(), 17);
2008        decoder.read_i32().unwrap();
2009        decoder.read_nullable_string().unwrap();
2010        assert!(decoder.read_tagged_fields().unwrap().is_empty());
2011        for _ in 0..3 {
2012            decoder.read_i32().unwrap();
2013        }
2014        decoder.read_i8().unwrap();
2015        decoder.read_i32().unwrap();
2016        decoder.read_i32().unwrap();
2017        assert_eq!(decoder.read_unsigned_varint().unwrap(), 2);
2018        assert_eq!(decoder.read_uuid().unwrap(), [7; 16]);
2019        assert_eq!(decoder.read_unsigned_varint().unwrap(), 2);
2020        decoder.read_i32().unwrap();
2021        decoder.read_i32().unwrap();
2022        decoder.read_i64().unwrap();
2023        decoder.read_i32().unwrap();
2024        decoder.read_i64().unwrap();
2025        decoder.read_i32().unwrap();
2026        let fields = decoder.read_tagged_fields().unwrap();
2027        assert_eq!(fields.len(), 1);
2028        assert_eq!(fields[0].tag, 0);
2029        assert_eq!(fields[0].data, vec![8; 16]);
2030        assert!(decoder.read_tagged_fields().unwrap().is_empty());
2031        assert_eq!(decoder.read_unsigned_varint().unwrap(), 1);
2032        assert!(decoder.read_compact_string().unwrap().is_empty());
2033        assert!(decoder.read_tagged_fields().unwrap().is_empty());
2034        assert!(decoder.is_empty());
2035    }
2036
2037    #[test]
2038    fn encodes_fetch_request_v18_high_watermark_after_directory_id() {
2039        let request = FetchRequestV18 {
2040            correlation_id: 16,
2041            client_id: None,
2042            cluster_id: None,
2043            replica_state: None,
2044            max_wait_ms: 500,
2045            min_bytes: 1,
2046            max_bytes: 1_048_576,
2047            isolation_level: 0,
2048            session_id: 0,
2049            session_epoch: -1,
2050            topics: vec![FetchTopicV18 {
2051                topic_id: [9; 16],
2052                partitions: vec![FetchPartitionV18 {
2053                    partition_index: 0,
2054                    current_leader_epoch: -1,
2055                    fetch_offset: 42,
2056                    last_fetched_epoch: -1,
2057                    log_start_offset: -1,
2058                    max_bytes: 1_048_576,
2059                    replica_directory_id: Some([10; 16]),
2060                    high_watermark: Some(100),
2061                }],
2062            }],
2063            forgotten_topics: Vec::new(),
2064            rack_id: String::new(),
2065        };
2066
2067        let bytes = request.encode().unwrap();
2068        let mut decoder = Decoder::new(&bytes);
2069        decoder.read_i16().unwrap();
2070        decoder.read_i16().unwrap();
2071        decoder.read_i32().unwrap();
2072        decoder.read_nullable_string().unwrap();
2073        decoder.read_tagged_fields().unwrap();
2074        for _ in 0..3 {
2075            decoder.read_i32().unwrap();
2076        }
2077        decoder.read_i8().unwrap();
2078        decoder.read_i32().unwrap();
2079        decoder.read_i32().unwrap();
2080        decoder.read_unsigned_varint().unwrap();
2081        decoder.read_uuid().unwrap();
2082        decoder.read_unsigned_varint().unwrap();
2083        decoder.read_i32().unwrap();
2084        decoder.read_i32().unwrap();
2085        decoder.read_i64().unwrap();
2086        decoder.read_i32().unwrap();
2087        decoder.read_i64().unwrap();
2088        decoder.read_i32().unwrap();
2089        let fields = decoder.read_tagged_fields().unwrap();
2090        assert_eq!(fields.len(), 2);
2091        assert_eq!(fields[0].tag, 0);
2092        assert_eq!(fields[0].data, vec![10; 16]);
2093        assert_eq!(fields[1].tag, 1);
2094        assert_eq!(fields[1].data, 100_i64.to_be_bytes());
2095        assert!(decoder.read_tagged_fields().unwrap().is_empty());
2096        assert_eq!(decoder.read_unsigned_varint().unwrap(), 1);
2097        assert!(decoder.read_compact_string().unwrap().is_empty());
2098        assert!(decoder.read_tagged_fields().unwrap().is_empty());
2099        assert!(decoder.is_empty());
2100    }
2101
2102    #[test]
2103    fn decodes_fetch_response_v4_with_aborted_transaction() {
2104        let mut bytes = Encoder::new();
2105        bytes.write_i32(0);
2106        bytes.write_i32(1);
2107        bytes.write_string("orders").unwrap();
2108        bytes.write_i32(1);
2109        bytes.write_i32(0);
2110        bytes.write_i16(0);
2111        bytes.write_i64(43);
2112        bytes.write_i64(42);
2113        bytes.write_i32(1);
2114        bytes.write_i64(7);
2115        bytes.write_i64(40);
2116        bytes.write_bytes(&[]).unwrap();
2117        let bytes = bytes.into_bytes();
2118
2119        let mut decoder = Decoder::new(&bytes);
2120        let response = FetchResponseV4::decode_body(&mut decoder).unwrap();
2121        let partition = &response.responses[0].partitions[0];
2122
2123        assert_eq!(partition.high_watermark, 43);
2124        assert_eq!(partition.last_stable_offset, 42);
2125        assert_eq!(partition.aborted_transactions[0].producer_id, 7);
2126        assert_eq!(partition.aborted_transactions[0].first_offset, 40);
2127        assert!(partition.records.is_empty());
2128        assert!(decoder.is_empty());
2129    }
2130
2131    #[test]
2132    fn decodes_fetch_response_v2_with_message_set() {
2133        let mut message = Encoder::new();
2134        message.write_i32(0);
2135        message.write_i8(1);
2136        message.write_i8(0);
2137        message.write_i64(123);
2138        message.write_nullable_bytes(Some(b"order-1")).unwrap();
2139        message.write_nullable_bytes(Some(b"created")).unwrap();
2140        let message = message.into_bytes();
2141
2142        let mut set = Encoder::new();
2143        set.write_i64(42);
2144        set.write_i32(i32::try_from(message.len()).unwrap());
2145        set.write_raw(&message);
2146        let set = set.into_bytes();
2147
2148        let mut bytes = Encoder::new();
2149        bytes.write_i32(0);
2150        bytes.write_i32(1);
2151        bytes.write_string("orders").unwrap();
2152        bytes.write_i32(1);
2153        bytes.write_i32(0);
2154        bytes.write_i16(0);
2155        bytes.write_i64(43);
2156        bytes.write_bytes(&set).unwrap();
2157        let bytes = bytes.into_bytes();
2158
2159        let mut decoder = Decoder::new(&bytes);
2160        let response = FetchResponseV2::decode_body(&mut decoder).unwrap();
2161        let record = MessageSetRecord {
2162            offset: 42,
2163            leader_epoch: -1,
2164            timestamp_ms: 123,
2165            key: Some(b"order-1".to_vec()),
2166            value: Some(b"created".to_vec()),
2167            headers: Vec::new(),
2168            producer_id: None,
2169            transactional: false,
2170            control: false,
2171        };
2172
2173        assert_eq!(response.throttle_time_ms, 0);
2174        assert_eq!(response.responses[0].partitions[0].high_watermark, 43);
2175        assert_eq!(response.responses[0].partitions[0].records, vec![record]);
2176        assert!(decoder.is_empty());
2177    }
2178
2179    #[test]
2180    fn decodes_fetch_response_v2_ignores_partial_trailing_message_set_entry() {
2181        let mut message = Encoder::new();
2182        message.write_i32(0);
2183        message.write_i8(1);
2184        message.write_i8(0);
2185        message.write_i64(123);
2186        message.write_nullable_bytes(Some(b"order-1")).unwrap();
2187        message.write_nullable_bytes(Some(b"created")).unwrap();
2188        let message = message.into_bytes();
2189
2190        let mut set = Encoder::new();
2191        set.write_i64(42);
2192        set.write_i32(i32::try_from(message.len()).unwrap());
2193        set.write_raw(&message);
2194        set.write_i64(-1);
2195        set.write_i32(61);
2196        set.write_raw(&[0; 22]);
2197        let set = set.into_bytes();
2198
2199        let mut bytes = Encoder::new();
2200        bytes.write_i32(0);
2201        bytes.write_i32(1);
2202        bytes.write_string("orders").unwrap();
2203        bytes.write_i32(1);
2204        bytes.write_i32(0);
2205        bytes.write_i16(0);
2206        bytes.write_i64(43);
2207        bytes.write_bytes(&set).unwrap();
2208        let bytes = bytes.into_bytes();
2209
2210        let mut decoder = Decoder::new(&bytes);
2211        let response = FetchResponseV2::decode_body(&mut decoder).unwrap();
2212
2213        assert_eq!(response.responses[0].partitions[0].records.len(), 1);
2214        assert_eq!(response.responses[0].partitions[0].records[0].offset, 42);
2215        assert!(decoder.is_empty());
2216    }
2217
2218    #[test]
2219    fn decodes_fetch_response_v2_with_record_batch() {
2220        let mut record = Vec::new();
2221        record.push(0);
2222        write_varlong(&mut record, 5);
2223        write_varint(&mut record, 0);
2224        write_varint(&mut record, 7);
2225        record.extend_from_slice(b"order-1");
2226        write_varint(&mut record, 7);
2227        record.extend_from_slice(b"created");
2228        write_varint(&mut record, 2);
2229        write_varint(&mut record, 6);
2230        record.extend_from_slice(b"source");
2231        write_varint(&mut record, 8);
2232        record.extend_from_slice(b"checkout");
2233        write_varint(&mut record, 9);
2234        record.extend_from_slice(b"tombstone");
2235        write_varint(&mut record, -1);
2236
2237        let mut batch = Encoder::new();
2238        batch.write_i32(0);
2239        batch.write_i8(2);
2240        batch.write_i32(0);
2241        batch.write_i16(0x10);
2242        batch.write_i32(0);
2243        batch.write_i64(1_000);
2244        batch.write_i64(1_005);
2245        batch.write_i64(7);
2246        batch.write_i16(-1);
2247        batch.write_i32(-1);
2248        batch.write_i32(1);
2249        let mut encoded_record = Vec::new();
2250        write_varint(&mut encoded_record, i32::try_from(record.len()).unwrap());
2251        encoded_record.extend_from_slice(&record);
2252        batch.write_raw(&encoded_record);
2253        let batch = batch.into_bytes();
2254
2255        let mut set = Encoder::new();
2256        set.write_i64(42);
2257        set.write_i32(i32::try_from(batch.len()).unwrap());
2258        set.write_raw(&batch);
2259        let set = set.into_bytes();
2260
2261        let mut bytes = Encoder::new();
2262        bytes.write_i32(0);
2263        bytes.write_i32(1);
2264        bytes.write_string("orders").unwrap();
2265        bytes.write_i32(1);
2266        bytes.write_i32(0);
2267        bytes.write_i16(0);
2268        bytes.write_i64(43);
2269        bytes.write_bytes(&set).unwrap();
2270        let bytes = bytes.into_bytes();
2271
2272        let mut decoder = Decoder::new(&bytes);
2273        let response = FetchResponseV2::decode_body(&mut decoder).unwrap();
2274        let record = MessageSetRecord {
2275            offset: 42,
2276            leader_epoch: 0,
2277            timestamp_ms: 1_005,
2278            key: Some(b"order-1".to_vec()),
2279            value: Some(b"created".to_vec()),
2280            headers: vec![
2281                RecordBatchHeader::new("source", Some(b"checkout".to_vec())),
2282                RecordBatchHeader::new("tombstone", None),
2283            ],
2284            producer_id: Some(7),
2285            transactional: true,
2286            control: false,
2287        };
2288
2289        assert_eq!(response.responses[0].partitions[0].records, vec![record]);
2290        assert!(decoder.is_empty());
2291    }
2292
2293    fn write_varint(output: &mut Vec<u8>, value: i32) {
2294        write_unsigned_varint(output, u64::from(((value << 1) ^ (value >> 31)) as u32));
2295    }
2296
2297    fn write_varlong(output: &mut Vec<u8>, value: i64) {
2298        write_unsigned_varint(output, ((value << 1) ^ (value >> 63)) as u64);
2299    }
2300
2301    fn write_unsigned_varint(output: &mut Vec<u8>, mut value: u64) {
2302        loop {
2303            let mut byte = (value & 0x7f) as u8;
2304            value >>= 7;
2305            if value != 0 {
2306                byte |= 0x80;
2307            }
2308            output.push(byte);
2309            if value == 0 {
2310                break;
2311            }
2312        }
2313    }
2314}