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