krabka_protocol/krabka/barrier/
trigger_barrier.rs1use bytes::{Buf, BufMut};
13
14use crate::{
15 Decode, Encode, ProtocolError, ProtocolRequest, UnknownTaggedFields,
16 krabka::{
17 barrier::cut::{BarrierCutTopic, BarrierMissingPartition},
18 common::{
19 I8_LEN, I16_LEN, I32_LEN, I64_LEN, array_len, check_version, decode_array,
20 encode_array, read_unknown_tagged_fields, unknown_tagged_fields_len,
21 write_unknown_tagged_fields,
22 },
23 },
24 primitives::{
25 fixed::{get_i8, get_i16, get_i32, get_i64, put_i8, put_i16, put_i32, put_i64},
26 string_bytes::{
27 compact_nullable_string_len, compact_string_len, get_compact_nullable_string_owned,
28 get_compact_string_owned, put_compact_nullable_string, put_compact_string,
29 },
30 },
31};
32
33pub const API_KEY: i16 = 1012;
35pub const MIN_VERSION: i16 = 0;
37pub const MAX_VERSION: i16 = 0;
39pub const FLEXIBLE_MIN: i16 = 0;
41
42#[inline]
47#[must_use]
48pub fn is_flexible(version: i16) -> bool {
49 version >= FLEXIBLE_MIN
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, Default)]
54pub struct TriggerBarrierRequest {
55 pub group: String,
57 pub timeout_ms: i32,
60 pub unknown_tagged_fields: UnknownTaggedFields,
62}
63
64impl Encode for TriggerBarrierRequest {
65 fn encode<B: BufMut>(&self, buf: &mut B, version: i16) -> Result<(), ProtocolError> {
66 check_version(API_KEY, version, MIN_VERSION, MAX_VERSION)?;
67 put_compact_string(buf, &self.group);
68 put_i32(buf, self.timeout_ms);
69 write_unknown_tagged_fields(buf, &self.unknown_tagged_fields);
70 Ok(())
71 }
72
73 fn encoded_len(&self, _version: i16) -> usize {
74 compact_string_len(&self.group)
75 + I32_LEN
76 + unknown_tagged_fields_len(&self.unknown_tagged_fields)
77 }
78}
79
80impl Decode<'_> for TriggerBarrierRequest {
81 fn decode<B: Buf>(buf: &mut B, version: i16) -> Result<Self, ProtocolError> {
82 check_version(API_KEY, version, MIN_VERSION, MAX_VERSION)?;
83 Ok(Self {
84 group: get_compact_string_owned(buf)?,
85 timeout_ms: get_i32(buf)?,
86 unknown_tagged_fields: read_unknown_tagged_fields(buf)?,
87 })
88 }
89}
90
91impl ProtocolRequest for TriggerBarrierRequest {
92 const API_KEY: i16 = API_KEY;
93 const MIN_VERSION: i16 = MIN_VERSION;
94 const MAX_VERSION: i16 = MAX_VERSION;
95 const LATEST_STABLE_VERSION: i16 = MAX_VERSION;
96 const FLEXIBLE_MIN: i16 = FLEXIBLE_MIN;
97
98 type Response = TriggerBarrierResponse;
99}
100
101#[derive(Debug, Clone, PartialEq, Eq, Default)]
103pub struct TriggerBarrierResponse {
104 pub throttle_time_ms: i32,
106 pub error_code: i16,
109 pub error_message: Option<String>,
112 pub epoch: i64,
115 pub status: i8,
120 pub topics: Vec<BarrierCutTopic>,
122 pub missing: Vec<BarrierMissingPartition>,
125 pub unknown_tagged_fields: UnknownTaggedFields,
127}
128
129impl Encode for TriggerBarrierResponse {
130 fn encode<B: BufMut>(&self, buf: &mut B, version: i16) -> Result<(), ProtocolError> {
131 check_version(API_KEY, version, MIN_VERSION, MAX_VERSION)?;
132 put_i32(buf, self.throttle_time_ms);
133 put_i16(buf, self.error_code);
134 put_compact_nullable_string(buf, self.error_message.as_deref());
135 put_i64(buf, self.epoch);
136 put_i8(buf, self.status);
137 encode_array(buf, &self.topics, version)?;
138 encode_array(buf, &self.missing, version)?;
139 write_unknown_tagged_fields(buf, &self.unknown_tagged_fields);
140 Ok(())
141 }
142
143 fn encoded_len(&self, version: i16) -> usize {
144 I32_LEN
145 + I16_LEN
146 + compact_nullable_string_len(self.error_message.as_deref())
147 + I64_LEN
148 + I8_LEN
149 + array_len(&self.topics, version)
150 + array_len(&self.missing, version)
151 + unknown_tagged_fields_len(&self.unknown_tagged_fields)
152 }
153}
154
155impl Decode<'_> for TriggerBarrierResponse {
156 fn decode<B: Buf>(buf: &mut B, version: i16) -> Result<Self, ProtocolError> {
157 check_version(API_KEY, version, MIN_VERSION, MAX_VERSION)?;
158 Ok(Self {
159 throttle_time_ms: get_i32(buf)?,
160 error_code: get_i16(buf)?,
161 error_message: get_compact_nullable_string_owned(buf)?,
162 epoch: get_i64(buf)?,
163 status: get_i8(buf)?,
164 topics: decode_array(buf, version)?,
165 missing: decode_array(buf, version)?,
166 unknown_tagged_fields: read_unknown_tagged_fields(buf)?,
167 })
168 }
169}
170
171#[cfg(test)]
172impl TriggerBarrierRequest {
173 fn populated() -> Self {
175 Self {
176 group: "orders".to_string(),
177 timeout_ms: 30_000,
178 unknown_tagged_fields: crate::krabka::test_support::sample_tagged_fields(),
179 }
180 }
181}
182
183#[cfg(test)]
184impl TriggerBarrierResponse {
185 fn populated() -> Self {
188 Self {
189 throttle_time_ms: 3,
190 error_code: 0,
191 error_message: Some("one partition had no leader".to_string()),
192 epoch: 41,
193 status: crate::krabka::barrier::CUT_STATUS_PARTIAL,
194 topics: super::test_support::sample_cut_topics(),
195 missing: super::test_support::sample_missing_partitions(),
196 unknown_tagged_fields: crate::krabka::test_support::sample_tagged_fields(),
197 }
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use assert2::assert;
204
205 use super::*;
206 use crate::krabka::{
207 barrier::{CUT_STATUS_COMPLETE, CUT_STATUS_PARTIAL, test_support::sample_cut_topics},
208 test_support::{roundtrip, unsupported_versions},
209 };
210
211 #[test]
212 fn default_roundtrips_all_versions() {
213 for version in MIN_VERSION..=MAX_VERSION {
214 roundtrip(&TriggerBarrierRequest::default(), version);
215 roundtrip(&TriggerBarrierResponse::default(), version);
216 }
217 }
218
219 #[test]
220 fn populated_roundtrips_all_versions() {
221 for version in MIN_VERSION..=MAX_VERSION {
222 roundtrip(&TriggerBarrierRequest::populated(), version);
223 roundtrip(&TriggerBarrierResponse::populated(), version);
224 }
225 }
226
227 #[test]
228 fn complete_cut_carries_no_missing_partitions() {
229 let response = TriggerBarrierResponse {
230 epoch: 7,
231 status: CUT_STATUS_COMPLETE,
232 topics: sample_cut_topics(),
233 ..TriggerBarrierResponse::default()
234 };
235 roundtrip(&response, MIN_VERSION);
236 assert!(response.missing.is_empty());
237 }
238
239 #[test]
240 fn status_codes_are_distinct() {
241 assert!(CUT_STATUS_COMPLETE != CUT_STATUS_PARTIAL);
242 }
243
244 #[test]
245 fn rejects_unsupported_versions() {
246 for version in unsupported_versions(MIN_VERSION, MAX_VERSION) {
247 let request = TriggerBarrierRequest::populated();
248 assert!(matches!(
249 request.encode(&mut Vec::<u8>::new(), version),
250 Err(ProtocolError::UnsupportedVersion {
251 api_key: API_KEY,
252 ..
253 })
254 ));
255 }
256 }
257}