1use crate::codec::{Decoder, Encoder};
2use crate::error::{Error, Result};
3
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub struct ConsumerProtocolSubscriptionV0 {
6 pub topics: Vec<String>,
7 pub user_data: Option<Vec<u8>>,
8}
9
10#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct ConsumerProtocolSubscriptionV1 {
16 pub topics: Vec<String>,
17 pub user_data: Option<Vec<u8>>,
18 pub owned_partitions: Vec<ConsumerProtocolTopicAssignment>,
19}
20
21impl ConsumerProtocolSubscriptionV1 {
22 pub fn encode(&self) -> Result<Vec<u8>> {
23 let mut encoder = Encoder::new();
24 encoder.write_i16(1);
25 encoder.write_array(Some(self.topics.as_slice()), |encoder, topic| {
26 encoder.write_string(topic)
27 })?;
28 encoder.write_nullable_bytes(self.user_data.as_deref())?;
29 encoder.write_array(
30 Some(self.owned_partitions.as_slice()),
31 |encoder, assignment| assignment.encode(encoder),
32 )?;
33 Ok(encoder.into_bytes())
34 }
35
36 pub fn decode(bytes: &[u8]) -> Result<Self> {
37 let mut decoder = Decoder::new(bytes);
38 expect_version("consumer protocol subscription", decoder.read_i16()?, 1)?;
39 Ok(Self {
40 topics: decoder
41 .read_array(
42 "consumer protocol subscription topics",
43 Decoder::read_string,
44 )?
45 .unwrap_or_default(),
46 user_data: decoder.read_nullable_bytes()?,
47 owned_partitions: decoder
48 .read_array(
49 "consumer protocol subscription owned partitions",
50 ConsumerProtocolTopicAssignment::decode,
51 )?
52 .unwrap_or_default(),
53 })
54 }
55}
56
57impl ConsumerProtocolSubscriptionV0 {
58 pub fn encode(&self) -> Result<Vec<u8>> {
59 let mut encoder = Encoder::new();
60 encoder.write_i16(0);
61 encoder.write_array(Some(self.topics.as_slice()), |encoder, topic| {
62 encoder.write_string(topic)
63 })?;
64 encoder.write_nullable_bytes(self.user_data.as_deref())?;
65 Ok(encoder.into_bytes())
66 }
67
68 pub fn decode(bytes: &[u8]) -> Result<Self> {
69 let mut decoder = Decoder::new(bytes);
70 expect_version_v0("consumer protocol subscription", decoder.read_i16()?)?;
71 let subscription = Self {
72 topics: decoder
73 .read_array(
74 "consumer protocol subscription topics",
75 Decoder::read_string,
76 )?
77 .unwrap_or_default(),
78 user_data: decoder.read_nullable_bytes()?,
79 };
80 Ok(subscription)
81 }
82}
83
84#[derive(Debug, Clone, PartialEq, Eq)]
85pub struct ConsumerProtocolAssignmentV0 {
86 pub assignments: Vec<ConsumerProtocolTopicAssignment>,
87 pub user_data: Option<Vec<u8>>,
88}
89
90impl ConsumerProtocolAssignmentV0 {
91 pub fn encode(&self) -> Result<Vec<u8>> {
92 let mut encoder = Encoder::new();
93 encoder.write_i16(0);
94 encoder.write_array(Some(self.assignments.as_slice()), |encoder, assignment| {
95 assignment.encode(encoder)
96 })?;
97 encoder.write_nullable_bytes(self.user_data.as_deref())?;
98 Ok(encoder.into_bytes())
99 }
100
101 pub fn decode(bytes: &[u8]) -> Result<Self> {
102 let mut decoder = Decoder::new(bytes);
103 expect_version_v0("consumer protocol assignment", decoder.read_i16()?)?;
104 let assignment = Self {
105 assignments: decoder
106 .read_array(
107 "consumer protocol assignment topics",
108 ConsumerProtocolTopicAssignment::decode,
109 )?
110 .unwrap_or_default(),
111 user_data: decoder.read_nullable_bytes()?,
112 };
113 Ok(assignment)
114 }
115}
116
117fn expect_version_v0(kind: &'static str, version: i16) -> Result<()> {
118 expect_version(kind, version, 0)
119}
120
121fn expect_version(kind: &'static str, version: i16, expected: i16) -> Result<()> {
122 if version == expected {
123 return Ok(());
124 }
125
126 Err(Error::UnsupportedVersion { kind, version })
127}
128
129#[derive(Debug, Clone, PartialEq, Eq)]
130pub struct ConsumerProtocolTopicAssignment {
131 pub topic: String,
132 pub partitions: Vec<i32>,
133}
134
135impl ConsumerProtocolTopicAssignment {
136 fn encode(&self, encoder: &mut Encoder) -> Result<()> {
137 encoder.write_string(&self.topic)?;
138 encoder.write_array(Some(self.partitions.as_slice()), |encoder, partition| {
139 encoder.write_i32(*partition);
140 Ok(())
141 })
142 }
143
144 fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
145 Ok(Self {
146 topic: decoder.read_string()?,
147 partitions: decoder
148 .read_array("consumer protocol assignment partitions", |decoder| {
149 decoder.read_i32()
150 })?
151 .unwrap_or_default(),
152 })
153 }
154}
155
156#[cfg(test)]
157#[allow(clippy::unwrap_used)]
158mod tests {
159 use super::{
160 ConsumerProtocolAssignmentV0, ConsumerProtocolSubscriptionV0,
161 ConsumerProtocolSubscriptionV1, ConsumerProtocolTopicAssignment,
162 };
163
164 #[test]
165 fn encodes_consumer_protocol_subscription_v0() {
166 let subscription = ConsumerProtocolSubscriptionV0 {
167 topics: vec!["orders".to_owned(), "payments".to_owned()],
168 user_data: None,
169 };
170
171 assert_eq!(
172 subscription.encode().unwrap(),
173 [
174 0, 0, 0, 0, 0, 2, 0, 6, b'o', b'r', b'd', b'e', b'r', b's', 0, 8, b'p', b'a', b'y', b'm', b'e', b'n', b't', b's', 0xff, 0xff, 0xff, 0xff, ]
180 );
181 }
182
183 #[test]
184 fn decodes_consumer_protocol_subscription_v0() {
185 let encoded = ConsumerProtocolSubscriptionV0 {
186 topics: vec!["orders".to_owned()],
187 user_data: Some(vec![1, 2, 3]),
188 }
189 .encode()
190 .unwrap();
191
192 let decoded = ConsumerProtocolSubscriptionV0::decode(&encoded).unwrap();
193
194 assert_eq!(
195 decoded,
196 ConsumerProtocolSubscriptionV0 {
197 topics: vec!["orders".to_owned()],
198 user_data: Some(vec![1, 2, 3]),
199 }
200 );
201 }
202
203 #[test]
204 fn rejects_unsupported_subscription_version() {
205 let error = ConsumerProtocolSubscriptionV0::decode(&[0, 1]).unwrap_err();
206
207 assert_eq!(
208 error,
209 crate::Error::UnsupportedVersion {
210 kind: "consumer protocol subscription",
211 version: 1,
212 }
213 );
214 }
215
216 #[test]
217 fn encodes_and_decodes_consumer_protocol_subscription_v1_with_owned_partitions() {
218 let subscription = ConsumerProtocolSubscriptionV1 {
219 topics: vec!["orders".to_owned()],
220 user_data: Some(vec![9]),
221 owned_partitions: vec![ConsumerProtocolTopicAssignment {
222 topic: "orders".to_owned(),
223 partitions: vec![1, 3],
224 }],
225 };
226
227 let encoded = subscription.encode().unwrap();
228 let decoded = ConsumerProtocolSubscriptionV1::decode(&encoded).unwrap();
229
230 assert_eq!(decoded, subscription);
231 }
232
233 #[test]
234 fn encodes_consumer_protocol_assignment_v0() {
235 let assignment = ConsumerProtocolAssignmentV0 {
236 assignments: vec![ConsumerProtocolTopicAssignment {
237 topic: "orders".to_owned(),
238 partitions: vec![0, 2],
239 }],
240 user_data: Some(vec![9]),
241 };
242
243 assert_eq!(
244 assignment.encode().unwrap(),
245 [
246 0, 0, 0, 0, 0, 1, 0, 6, b'o', b'r', b'd', b'e', b'r', b's', 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1, 9, ]
254 );
255 }
256
257 #[test]
258 fn decodes_consumer_protocol_assignment_v0() {
259 let encoded = ConsumerProtocolAssignmentV0 {
260 assignments: vec![ConsumerProtocolTopicAssignment {
261 topic: "orders".to_owned(),
262 partitions: vec![0, 2],
263 }],
264 user_data: None,
265 }
266 .encode()
267 .unwrap();
268
269 let decoded = ConsumerProtocolAssignmentV0::decode(&encoded).unwrap();
270
271 assert_eq!(
272 decoded,
273 ConsumerProtocolAssignmentV0 {
274 assignments: vec![ConsumerProtocolTopicAssignment {
275 topic: "orders".to_owned(),
276 partitions: vec![0, 2],
277 }],
278 user_data: None,
279 }
280 );
281 }
282}