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