1use bytes::BufMut;
3
4use crate::{
5 DecodeBorrow, Encode, ProtocolError, UnknownTaggedFields,
6 owned::consumer_group_heartbeat_request,
7 primitives::{
8 array::{
9 array_len_prefix_len, get_array_len, get_nullable_array_len,
10 nullable_array_len_prefix_len, put_array_len, put_nullable_array_len,
11 },
12 fixed::{get_i32, put_i32},
13 string_bytes::{
14 compact_nullable_string_len, compact_string_len, nullable_string_len,
15 put_compact_nullable_string, put_compact_string, put_nullable_string, put_string,
16 string_len,
17 },
18 string_bytes_borrowed::{
19 get_compact_nullable_string_borrowed, get_compact_string_borrowed,
20 get_nullable_string_borrowed, get_string_borrowed,
21 },
22 uuid::{Uuid, get_uuid, put_uuid},
23 },
24 tagged_fields::{WriteTaggedFields, read_tagged_fields, tagged_fields_len},
25};
26pub const API_KEY: i16 = 68;
27pub const MIN_VERSION: i16 = 0;
28pub const MAX_VERSION: i16 = 1;
29pub const LATEST_STABLE_VERSION: i16 = 1;
30pub const FLEXIBLE_MIN: i16 = 0;
31#[inline]
32#[must_use]
33pub fn is_flexible(version: i16) -> bool {
34 version >= FLEXIBLE_MIN
35}
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct ConsumerGroupHeartbeatRequest<'a> {
38 pub group_id: &'a str,
39 pub member_id: &'a str,
40 pub member_epoch: i32,
41 pub instance_id: Option<&'a str>,
42 pub rack_id: Option<&'a str>,
43 pub rebalance_timeout_ms: i32,
44 pub subscribed_topic_names: Option<Vec<&'a str>>,
45 pub subscribed_topic_regex: Option<&'a str>,
46 pub server_assignor: Option<&'a str>,
47 pub topic_partitions: Option<Vec<TopicPartitions>>,
48 pub unknown_tagged_fields: UnknownTaggedFields,
49}
50impl Default for ConsumerGroupHeartbeatRequest<'_> {
51 fn default() -> Self {
52 Self {
53 group_id: "",
54 member_id: "",
55 member_epoch: 0i32,
56 instance_id: None,
57 rack_id: None,
58 rebalance_timeout_ms: -1i32,
59 subscribed_topic_names: None,
60 subscribed_topic_regex: None,
61 server_assignor: None,
62 topic_partitions: None,
63 unknown_tagged_fields: UnknownTaggedFields::default(),
64 }
65 }
66}
67impl<'a> ConsumerGroupHeartbeatRequest<'a> {
68 #[must_use]
69 pub fn to_owned(&self) -> consumer_group_heartbeat_request::ConsumerGroupHeartbeatRequest {
70 consumer_group_heartbeat_request::ConsumerGroupHeartbeatRequest {
71 group_id: (self.group_id).to_owned(),
72 member_id: (self.member_id).to_owned(),
73 member_epoch: (self.member_epoch),
74 instance_id: (self.instance_id).map(str::to_owned),
75 rack_id: (self.rack_id).map(str::to_owned),
76 rebalance_timeout_ms: (self.rebalance_timeout_ms),
77 subscribed_topic_names: (self.subscribed_topic_names)
78 .as_ref()
79 .map(|v| v.iter().map(|&s| s.to_owned()).collect()),
80 subscribed_topic_regex: (self.subscribed_topic_regex).map(str::to_owned),
81 server_assignor: (self.server_assignor).map(str::to_owned),
82 topic_partitions: (self.topic_partitions)
83 .as_ref()
84 .map(|v| v.iter().map(TopicPartitions::to_owned).collect()),
85 unknown_tagged_fields: self.unknown_tagged_fields.clone(),
86 }
87 }
88 fn encode_field_0<B: BufMut>(
89 &self,
90 buf: &mut B,
91 version: i16,
92 flex: bool,
93 ) -> Result<(), ProtocolError> {
94 if version >= 0 {
95 if flex {
96 put_compact_string(buf, self.group_id)?;
97 } else {
98 put_string(buf, self.group_id)?;
99 }
100 }
101 Ok(())
102 }
103 fn encode_field_1<B: BufMut>(
104 &self,
105 buf: &mut B,
106 version: i16,
107 flex: bool,
108 ) -> Result<(), ProtocolError> {
109 if version >= 0 {
110 if flex {
111 put_compact_string(buf, self.member_id)?;
112 } else {
113 put_string(buf, self.member_id)?;
114 }
115 }
116 Ok(())
117 }
118 fn encode_field_2<B: BufMut>(&self, buf: &mut B, version: i16, _flex: bool) {
119 if version >= 0 {
120 put_i32(buf, self.member_epoch);
121 }
122 }
123 fn encode_field_3<B: BufMut>(
124 &self,
125 buf: &mut B,
126 version: i16,
127 flex: bool,
128 ) -> Result<(), ProtocolError> {
129 if version >= 0 {
130 if flex {
131 put_compact_nullable_string(buf, self.instance_id)?;
132 } else {
133 put_nullable_string(buf, self.instance_id)?;
134 }
135 }
136 Ok(())
137 }
138 fn encode_field_4<B: BufMut>(
139 &self,
140 buf: &mut B,
141 version: i16,
142 flex: bool,
143 ) -> Result<(), ProtocolError> {
144 if version >= 0 {
145 if flex {
146 put_compact_nullable_string(buf, self.rack_id)?;
147 } else {
148 put_nullable_string(buf, self.rack_id)?;
149 }
150 }
151 Ok(())
152 }
153 fn encode_field_5<B: BufMut>(&self, buf: &mut B, version: i16, _flex: bool) {
154 if version >= 0 {
155 put_i32(buf, self.rebalance_timeout_ms);
156 }
157 }
158 fn encode_field_6<B: BufMut>(
159 &self,
160 buf: &mut B,
161 version: i16,
162 flex: bool,
163 ) -> Result<(), ProtocolError> {
164 if version >= 0 {
165 {
166 let len = (self.subscribed_topic_names).as_ref().map(Vec::len);
167 put_nullable_array_len(buf, len, flex)?;
168 if let Some(v) = &self.subscribed_topic_names {
169 for it in v {
170 if flex {
171 put_compact_string(buf, it)?;
172 } else {
173 put_string(buf, it)?;
174 }
175 }
176 }
177 }
178 }
179 Ok(())
180 }
181 fn encode_field_7<B: BufMut>(
182 &self,
183 buf: &mut B,
184 version: i16,
185 flex: bool,
186 ) -> Result<(), ProtocolError> {
187 if version >= 1 {
188 if flex {
189 put_compact_nullable_string(buf, self.subscribed_topic_regex)?;
190 } else {
191 put_nullable_string(buf, self.subscribed_topic_regex)?;
192 }
193 }
194 Ok(())
195 }
196 fn encode_field_8<B: BufMut>(
197 &self,
198 buf: &mut B,
199 version: i16,
200 flex: bool,
201 ) -> Result<(), ProtocolError> {
202 if version >= 0 {
203 if flex {
204 put_compact_nullable_string(buf, self.server_assignor)?;
205 } else {
206 put_nullable_string(buf, self.server_assignor)?;
207 }
208 }
209 Ok(())
210 }
211 fn encode_field_9<B: BufMut>(
212 &self,
213 buf: &mut B,
214 version: i16,
215 flex: bool,
216 ) -> Result<(), ProtocolError> {
217 if version >= 0 {
218 {
219 let len = (self.topic_partitions).as_ref().map(Vec::len);
220 put_nullable_array_len(buf, len, flex)?;
221 if let Some(v) = &self.topic_partitions {
222 for it in v {
223 it.encode(buf, version)?;
224 }
225 }
226 }
227 }
228 Ok(())
229 }
230 fn encode_tagged_fields<B: BufMut>(
231 &self,
232 buf: &mut B,
233 _version: i16,
234 flex: bool,
235 ) -> Result<(), ProtocolError> {
236 if flex {
237 let tagged = WriteTaggedFields::new();
238 tagged.write(buf, &self.unknown_tagged_fields)?;
239 }
240 Ok(())
241 }
242 fn decode_field_0(
243 out: &mut Self,
244 buf: &mut &'a [u8],
245 version: i16,
246 flex: bool,
247 ) -> Result<(), ProtocolError> {
248 if version >= 0 {
249 out.group_id = if flex {
250 get_compact_string_borrowed(buf)?
251 } else {
252 get_string_borrowed(buf)?
253 };
254 }
255 Ok(())
256 }
257 fn decode_field_1(
258 out: &mut Self,
259 buf: &mut &'a [u8],
260 version: i16,
261 flex: bool,
262 ) -> Result<(), ProtocolError> {
263 if version >= 0 {
264 out.member_id = if flex {
265 get_compact_string_borrowed(buf)?
266 } else {
267 get_string_borrowed(buf)?
268 };
269 }
270 Ok(())
271 }
272 fn decode_field_2(
273 out: &mut Self,
274 buf: &mut &'a [u8],
275 version: i16,
276 _flex: bool,
277 ) -> Result<(), ProtocolError> {
278 if version >= 0 {
279 out.member_epoch = get_i32(buf)?;
280 }
281 Ok(())
282 }
283 fn decode_field_3(
284 out: &mut Self,
285 buf: &mut &'a [u8],
286 version: i16,
287 flex: bool,
288 ) -> Result<(), ProtocolError> {
289 if version >= 0 {
290 out.instance_id = if flex {
291 get_compact_nullable_string_borrowed(buf)?
292 } else {
293 get_nullable_string_borrowed(buf)?
294 };
295 }
296 Ok(())
297 }
298 fn decode_field_4(
299 out: &mut Self,
300 buf: &mut &'a [u8],
301 version: i16,
302 flex: bool,
303 ) -> Result<(), ProtocolError> {
304 if version >= 0 {
305 out.rack_id = if flex {
306 get_compact_nullable_string_borrowed(buf)?
307 } else {
308 get_nullable_string_borrowed(buf)?
309 };
310 }
311 Ok(())
312 }
313 fn decode_field_5(
314 out: &mut Self,
315 buf: &mut &'a [u8],
316 version: i16,
317 _flex: bool,
318 ) -> Result<(), ProtocolError> {
319 if version >= 0 {
320 out.rebalance_timeout_ms = get_i32(buf)?;
321 }
322 Ok(())
323 }
324 fn decode_field_6(
325 out: &mut Self,
326 buf: &mut &'a [u8],
327 version: i16,
328 flex: bool,
329 ) -> Result<(), ProtocolError> {
330 if version >= 0 {
331 out.subscribed_topic_names = {
332 let opt = get_nullable_array_len(buf, flex)?;
333 match opt {
334 None => None,
335 Some(n) => {
336 let mut v = Vec::with_capacity(n);
337 for _ in 0..n {
338 v.push(if flex {
339 get_compact_string_borrowed(buf)?
340 } else {
341 get_string_borrowed(buf)?
342 });
343 }
344 Some(v)
345 }
346 }
347 };
348 }
349 Ok(())
350 }
351 fn decode_field_7(
352 out: &mut Self,
353 buf: &mut &'a [u8],
354 version: i16,
355 flex: bool,
356 ) -> Result<(), ProtocolError> {
357 if version >= 1 {
358 out.subscribed_topic_regex = if flex {
359 get_compact_nullable_string_borrowed(buf)?
360 } else {
361 get_nullable_string_borrowed(buf)?
362 };
363 }
364 Ok(())
365 }
366 fn decode_field_8(
367 out: &mut Self,
368 buf: &mut &'a [u8],
369 version: i16,
370 flex: bool,
371 ) -> Result<(), ProtocolError> {
372 if version >= 0 {
373 out.server_assignor = if flex {
374 get_compact_nullable_string_borrowed(buf)?
375 } else {
376 get_nullable_string_borrowed(buf)?
377 };
378 }
379 Ok(())
380 }
381 fn decode_field_9(
382 out: &mut Self,
383 buf: &mut &'a [u8],
384 version: i16,
385 flex: bool,
386 ) -> Result<(), ProtocolError> {
387 if version >= 0 {
388 out.topic_partitions = {
389 let opt = get_nullable_array_len(buf, flex)?;
390 match opt {
391 None => None,
392 Some(n) => {
393 let mut v = Vec::with_capacity(n);
394 for _ in 0..n {
395 v.push(TopicPartitions::decode_borrow(buf, version)?);
396 }
397 Some(v)
398 }
399 }
400 };
401 }
402 Ok(())
403 }
404 fn decode_tagged_fields(
405 out: &mut Self,
406 buf: &mut &'a [u8],
407 _version: i16,
408 flex: bool,
409 ) -> Result<(), ProtocolError> {
410 if flex {
411 out.unknown_tagged_fields = read_tagged_fields(buf, |_tag, _payload| Ok(false))?;
412 }
413 Ok(())
414 }
415}
416impl Encode for ConsumerGroupHeartbeatRequest<'_> {
417 fn encode<B: BufMut>(&self, buf: &mut B, version: i16) -> Result<(), ProtocolError> {
418 if !(MIN_VERSION..=MAX_VERSION).contains(&version) {
419 return Err(ProtocolError::UnsupportedVersion {
420 api_key: API_KEY,
421 version,
422 });
423 }
424 let flex = is_flexible(version);
425 self.encode_field_0(buf, version, flex)?;
426 self.encode_field_1(buf, version, flex)?;
427 self.encode_field_2(buf, version, flex);
428 self.encode_field_3(buf, version, flex)?;
429 self.encode_field_4(buf, version, flex)?;
430 self.encode_field_5(buf, version, flex);
431 self.encode_field_6(buf, version, flex)?;
432 self.encode_field_7(buf, version, flex)?;
433 self.encode_field_8(buf, version, flex)?;
434 self.encode_field_9(buf, version, flex)?;
435 self.encode_tagged_fields(buf, version, flex)?;
436 Ok(())
437 }
438 fn encoded_len(&self, version: i16) -> usize {
439 let flex = is_flexible(version);
440 let mut n: usize = 0;
441 if version >= 0 {
442 n += if flex {
443 compact_string_len(self.group_id)
444 } else {
445 string_len(self.group_id)
446 };
447 }
448 if version >= 0 {
449 n += if flex {
450 compact_string_len(self.member_id)
451 } else {
452 string_len(self.member_id)
453 };
454 }
455 if version >= 0 {
456 n += 4;
457 }
458 if version >= 0 {
459 n += if flex {
460 compact_nullable_string_len(self.instance_id)
461 } else {
462 nullable_string_len(self.instance_id)
463 };
464 }
465 if version >= 0 {
466 n += if flex {
467 compact_nullable_string_len(self.rack_id)
468 } else {
469 nullable_string_len(self.rack_id)
470 };
471 }
472 if version >= 0 {
473 n += 4;
474 }
475 if version >= 0 {
476 n += {
477 let opt: Option<&Vec<_>> = (self.subscribed_topic_names).as_ref();
478 let prefix = nullable_array_len_prefix_len(opt.map(Vec::len), flex);
479 let body: usize = opt.map_or(0, |v| {
480 v.iter()
481 .map(|it| {
482 if flex {
483 compact_string_len(it)
484 } else {
485 string_len(it)
486 }
487 })
488 .sum()
489 });
490 prefix + body
491 };
492 }
493 if version >= 1 {
494 n += if flex {
495 compact_nullable_string_len(self.subscribed_topic_regex)
496 } else {
497 nullable_string_len(self.subscribed_topic_regex)
498 };
499 }
500 if version >= 0 {
501 n += if flex {
502 compact_nullable_string_len(self.server_assignor)
503 } else {
504 nullable_string_len(self.server_assignor)
505 };
506 }
507 if version >= 0 {
508 n += {
509 let opt: Option<&Vec<_>> = (self.topic_partitions).as_ref();
510 let prefix = nullable_array_len_prefix_len(opt.map(Vec::len), flex);
511 let body: usize =
512 opt.map_or(0, |v| v.iter().map(|it| it.encoded_len(version)).sum());
513 prefix + body
514 };
515 }
516 if flex {
517 let known_pairs: Vec<(u32, usize)> = Vec::new();
518 n += tagged_fields_len(&known_pairs, &self.unknown_tagged_fields);
519 }
520 n
521 }
522}
523impl<'de> DecodeBorrow<'de> for ConsumerGroupHeartbeatRequest<'de> {
524 fn decode_borrow(buf: &mut &'de [u8], version: i16) -> Result<Self, ProtocolError> {
525 if !(MIN_VERSION..=MAX_VERSION).contains(&version) {
526 return Err(ProtocolError::UnsupportedVersion {
527 api_key: API_KEY,
528 version,
529 });
530 }
531 let flex = is_flexible(version);
532 let mut out = Self::default();
533 Self::decode_field_0(&mut out, buf, version, flex)?;
534 Self::decode_field_1(&mut out, buf, version, flex)?;
535 Self::decode_field_2(&mut out, buf, version, flex)?;
536 Self::decode_field_3(&mut out, buf, version, flex)?;
537 Self::decode_field_4(&mut out, buf, version, flex)?;
538 Self::decode_field_5(&mut out, buf, version, flex)?;
539 Self::decode_field_6(&mut out, buf, version, flex)?;
540 Self::decode_field_7(&mut out, buf, version, flex)?;
541 Self::decode_field_8(&mut out, buf, version, flex)?;
542 Self::decode_field_9(&mut out, buf, version, flex)?;
543 Self::decode_tagged_fields(&mut out, buf, version, flex)?;
544 Ok(out)
545 }
546}
547#[cfg(test)]
548impl ConsumerGroupHeartbeatRequest<'_> {
549 #[must_use]
550 pub fn populated(version: i16) -> Self {
551 let mut m = Self::default();
552 if version >= 0 {
553 m.group_id = "x";
554 }
555 if version >= 0 {
556 m.member_id = "x";
557 }
558 if version >= 0 {
559 m.member_epoch = 1i32;
560 }
561 if version >= 0 {
562 m.instance_id = Some("x");
563 }
564 if version >= 0 {
565 m.rack_id = Some("x");
566 }
567 if version >= 0 {
568 m.rebalance_timeout_ms = 1i32;
569 }
570 if version >= 0 {
571 m.subscribed_topic_names = Some(vec!["x"]);
572 }
573 if version >= 1 {
574 m.subscribed_topic_regex = Some("x");
575 }
576 if version >= 0 {
577 m.server_assignor = Some("x");
578 }
579 if version >= 0 {
580 m.topic_partitions = Some(vec![TopicPartitions::populated(version)]);
581 }
582 m
583 }
584}
585#[derive(Debug, Clone, PartialEq, Eq, Default)]
586pub struct TopicPartitions {
587 pub topic_id: Uuid,
588 pub partitions: Vec<i32>,
589 pub unknown_tagged_fields: UnknownTaggedFields,
590}
591impl TopicPartitions {
592 #[must_use]
593 pub fn to_owned(&self) -> consumer_group_heartbeat_request::TopicPartitions {
594 consumer_group_heartbeat_request::TopicPartitions {
595 topic_id: (self.topic_id),
596 partitions: (self.partitions).clone(),
597 unknown_tagged_fields: self.unknown_tagged_fields.clone(),
598 }
599 }
600}
601impl Encode for TopicPartitions {
602 fn encode<B: BufMut>(&self, buf: &mut B, version: i16) -> Result<(), ProtocolError> {
603 let flex = version >= 0;
604 if version >= 0 {
605 put_uuid(buf, self.topic_id);
606 }
607 if version >= 0 {
608 {
609 put_array_len(buf, (self.partitions).len(), flex)?;
610 for it in &self.partitions {
611 put_i32(buf, *it);
612 }
613 }
614 }
615 if flex {
616 let tagged = WriteTaggedFields::new();
617 tagged.write(buf, &self.unknown_tagged_fields)?;
618 }
619 Ok(())
620 }
621 fn encoded_len(&self, version: i16) -> usize {
622 let flex = version >= 0;
623 let mut n: usize = 0;
624 if version >= 0 {
625 n += 16;
626 }
627 if version >= 0 {
628 n += {
629 let prefix = array_len_prefix_len((self.partitions).len(), flex);
630 let body: usize = (self.partitions).iter().map(|_| 4).sum();
631 prefix + body
632 };
633 }
634 if flex {
635 let known_pairs: Vec<(u32, usize)> = Vec::new();
636 n += tagged_fields_len(&known_pairs, &self.unknown_tagged_fields);
637 }
638 n
639 }
640}
641impl<'de> DecodeBorrow<'de> for TopicPartitions {
642 fn decode_borrow(buf: &mut &'de [u8], version: i16) -> Result<Self, ProtocolError> {
643 let flex = version >= 0;
644 let mut out = Self::default();
645 if version >= 0 {
646 out.topic_id = get_uuid(buf)?;
647 }
648 if version >= 0 {
649 out.partitions = {
650 let n = get_array_len(buf, flex)?;
651 let mut v = Vec::with_capacity(n);
652 for _ in 0..n {
653 v.push(get_i32(buf)?);
654 }
655 v
656 };
657 }
658 if flex {
659 out.unknown_tagged_fields = read_tagged_fields(buf, |_tag, _payload| Ok(false))?;
660 }
661 Ok(out)
662 }
663}
664#[cfg(test)]
665impl TopicPartitions {
666 #[must_use]
667 pub fn populated(version: i16) -> Self {
668 let mut m = Self::default();
669 if version >= 0 {
670 m.topic_id = Uuid([1u8; 16]);
671 }
672 if version >= 0 {
673 m.partitions = vec![1i32];
674 }
675 m
676 }
677}