1use crate::codec::{Decoder, Encoder};
2use crate::error::Result;
3use crate::header::RequestHeader;
4
5pub const API_KEY: i16 = 32;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct DescribeConfigsRequestV1 {
9 pub correlation_id: i32,
10 pub client_id: Option<String>,
11 pub resources: Vec<DescribeConfigsResourceV1>,
12 pub include_synonyms: bool,
13}
14
15impl DescribeConfigsRequestV1 {
16 pub fn encode(&self) -> Result<Vec<u8>> {
17 let mut encoder = Encoder::new();
18 RequestHeader {
19 api_key: API_KEY,
20 api_version: 1,
21 correlation_id: self.correlation_id,
22 client_id: self.client_id.clone(),
23 }
24 .encode_v1(&mut encoder)?;
25 encoder.write_array(Some(&self.resources), |encoder, resource| {
26 resource.encode(encoder)
27 })?;
28 encoder.write_bool(self.include_synonyms);
29 Ok(encoder.into_bytes())
30 }
31}
32
33#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct DescribeConfigsResourceV1 {
35 pub resource_type: i8,
36 pub resource_name: String,
37 pub configuration_keys: Option<Vec<String>>,
38}
39
40impl DescribeConfigsResourceV1 {
41 fn encode(&self, encoder: &mut Encoder) -> Result<()> {
42 encoder.write_i8(self.resource_type);
43 encoder.write_string(&self.resource_name)?;
44 encoder.write_array(
45 self.configuration_keys.as_deref(),
46 |encoder, configuration_key| encoder.write_string(configuration_key),
47 )
48 }
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub struct DescribeConfigsResponseV1 {
53 pub throttle_time_ms: i32,
54 pub results: Vec<DescribeConfigsResultV1>,
55}
56
57impl DescribeConfigsResponseV1 {
58 pub fn decode_body(decoder: &mut Decoder<'_>) -> Result<Self> {
59 Ok(Self {
60 throttle_time_ms: decoder.read_i32()?,
61 results: decoder
62 .read_array("describe configs results", DescribeConfigsResultV1::decode)?
63 .unwrap_or_default(),
64 })
65 }
66}
67
68#[derive(Debug, Clone, PartialEq, Eq)]
69pub struct DescribeConfigsResultV1 {
70 pub error_code: i16,
71 pub error_message: Option<String>,
72 pub resource_type: i8,
73 pub resource_name: String,
74 pub configs: Vec<DescribeConfigsEntryV1>,
75}
76
77impl DescribeConfigsResultV1 {
78 fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
79 Ok(Self {
80 error_code: decoder.read_i16()?,
81 error_message: decoder.read_nullable_string()?,
82 resource_type: decoder.read_i8()?,
83 resource_name: decoder.read_string()?,
84 configs: decoder
85 .read_array("describe configs entries", DescribeConfigsEntryV1::decode)?
86 .unwrap_or_default(),
87 })
88 }
89}
90
91#[derive(Debug, Clone, PartialEq, Eq)]
92pub struct DescribeConfigsEntryV1 {
93 pub name: String,
94 pub value: Option<String>,
95 pub read_only: bool,
96 pub config_source: i8,
97 pub is_sensitive: bool,
98 pub synonyms: Vec<DescribeConfigsSynonymV1>,
99}
100
101impl DescribeConfigsEntryV1 {
102 fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
103 Ok(Self {
104 name: decoder.read_string()?,
105 value: decoder.read_nullable_string()?,
106 read_only: decoder.read_bool()?,
107 config_source: decoder.read_i8()?,
108 is_sensitive: decoder.read_bool()?,
109 synonyms: decoder
110 .read_array(
111 "describe configs synonyms",
112 DescribeConfigsSynonymV1::decode,
113 )?
114 .unwrap_or_default(),
115 })
116 }
117}
118
119#[derive(Debug, Clone, PartialEq, Eq)]
120pub struct DescribeConfigsSynonymV1 {
121 pub name: String,
122 pub value: Option<String>,
123 pub source: i8,
124}
125
126impl DescribeConfigsSynonymV1 {
127 fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
128 Ok(Self {
129 name: decoder.read_string()?,
130 value: decoder.read_nullable_string()?,
131 source: decoder.read_i8()?,
132 })
133 }
134}
135
136#[cfg(test)]
137#[allow(clippy::unwrap_used)]
138mod tests {
139 use super::{
140 DescribeConfigsRequestV1, DescribeConfigsResourceV1, DescribeConfigsResponseV1, API_KEY,
141 };
142 use crate::codec::Decoder;
143
144 #[test]
145 fn encodes_describe_configs_v1_request() {
146 let request = DescribeConfigsRequestV1 {
147 correlation_id: 12,
148 client_id: Some("kafrust".to_owned()),
149 resources: vec![DescribeConfigsResourceV1 {
150 resource_type: 2,
151 resource_name: "orders".to_owned(),
152 configuration_keys: Some(vec!["cleanup.policy".to_owned()]),
153 }],
154 include_synonyms: true,
155 };
156
157 assert_eq!(
158 request.encode().unwrap(),
159 [
160 0, 32, 0, 1, 0, 0, 0, 12, 0, 7, b'k', b'a', b'f', b'r', b'u', b's', b't', 0, 0, 0, 1, 2, 0, 6, b'o', b'r', b'd', b'e', b'r', b's', 0, 0, 0, 1, 0, 14, b'c', b'l', b'e', b'a', b'n', b'u', b'p', b'.', b'p', b'o', b'l', b'i',
169 b'c', b'y', 1, ]
172 );
173 assert_eq!(API_KEY, 32);
174 }
175
176 #[test]
177 fn decodes_describe_configs_v1_response() {
178 let bytes = [
179 0, 0, 0, 9, 0, 0, 0, 1, 0, 0, 0xff, 0xff, 2, 0, 6, b'o', b'r', b'd', b'e', b'r', b's', 0, 0, 0, 1, 0, 14, b'c', b'l', b'e', b'a', b'n', b'u', b'p', b'.', b'p', b'o', b'l', b'i', b'c',
187 b'y', 0, 7, b'c', b'o', b'm', b'p', b'a', b'c', b't', 0, 1, 0, 0, 0, 0, 1, 0, 14, b'c', b'l', b'e', b'a', b'n', b'u', b'p', b'.', b'p', b'o', b'l', b'i', b'c',
194 b'y', 0, 6, b'd', b'e', b'l', b'e', b't', b'e', 5, ];
198 let mut decoder = Decoder::new(&bytes);
199
200 let response = DescribeConfigsResponseV1::decode_body(&mut decoder).unwrap();
201
202 assert_eq!(response.throttle_time_ms, 9);
203 assert_eq!(response.results.len(), 1);
204 let result = &response.results[0];
205 assert_eq!(result.resource_type, 2);
206 assert_eq!(result.resource_name, "orders");
207 assert_eq!(result.configs.len(), 1);
208 assert_eq!(result.configs[0].name, "cleanup.policy");
209 assert_eq!(result.configs[0].value.as_deref(), Some("compact"));
210 assert_eq!(result.configs[0].config_source, 1);
211 assert_eq!(result.configs[0].synonyms[0].source, 5);
212 assert!(decoder.is_empty());
213 }
214}