diff --git a/protocol_codegen/src/main.rs b/protocol_codegen/src/main.rs index b64c78a..3a4013e 100644 --- a/protocol_codegen/src/main.rs +++ b/protocol_codegen/src/main.rs @@ -31,9 +31,9 @@ fn main() -> Result<(), Error> { }; // Checkout the release commit - // https://github.com/apache/kafka/releases/tag/3.8.0 + // https://github.com/apache/kafka/releases/tag/3.9.0 // checking out a tag with git2 is annoying -- we pin to the tag's commit sha instead - let release_commit = "771b9576b00ecf5b64ab6e8bedf04156fbdb5cd6"; + let release_commit = "84caaa6e9da06435411510a81fa321d4f99c351f"; println!("Checking out release {}", release_commit); let oid = Oid::from_str(release_commit).unwrap(); let commit = repo diff --git a/src/messages.rs b/src/messages.rs index a44cf4c..ba5f153 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -277,6 +277,15 @@ pub use list_client_metrics_resources_request::ListClientMetricsResourcesRequest pub mod describe_topic_partitions_request; pub use describe_topic_partitions_request::DescribeTopicPartitionsRequest; +pub mod add_raft_voter_request; +pub use add_raft_voter_request::AddRaftVoterRequest; + +pub mod remove_raft_voter_request; +pub use remove_raft_voter_request::RemoveRaftVoterRequest; + +pub mod update_raft_voter_request; +pub use update_raft_voter_request::UpdateRaftVoterRequest; + pub mod produce_response; pub use produce_response::ProduceResponse; @@ -505,6 +514,15 @@ pub use list_client_metrics_resources_response::ListClientMetricsResourcesRespon pub mod describe_topic_partitions_response; pub use describe_topic_partitions_response::DescribeTopicPartitionsResponse; +pub mod add_raft_voter_response; +pub use add_raft_voter_response::AddRaftVoterResponse; + +pub mod remove_raft_voter_response; +pub use remove_raft_voter_response::RemoveRaftVoterResponse; + +pub mod update_raft_voter_response; +pub use update_raft_voter_response::UpdateRaftVoterResponse; + #[cfg(all(feature = "client", feature = "broker"))] impl Request for ProduceRequest { const KEY: i16 = 0; @@ -961,6 +979,24 @@ impl Request for DescribeTopicPartitionsRequest { type Response = DescribeTopicPartitionsResponse; } +#[cfg(all(feature = "client", feature = "broker"))] +impl Request for AddRaftVoterRequest { + const KEY: i16 = 80; + type Response = AddRaftVoterResponse; +} + +#[cfg(all(feature = "client", feature = "broker"))] +impl Request for RemoveRaftVoterRequest { + const KEY: i16 = 81; + type Response = RemoveRaftVoterResponse; +} + +#[cfg(all(feature = "client", feature = "broker"))] +impl Request for UpdateRaftVoterRequest { + const KEY: i16 = 82; + type Response = UpdateRaftVoterResponse; +} + /// Valid API keys in the Kafka protocol. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ApiKey { @@ -1116,6 +1152,12 @@ pub enum ApiKey { ListClientMetricsResources = 74, /// API key for request DescribeTopicPartitionsRequest DescribeTopicPartitions = 75, + /// API key for request AddRaftVoterRequest + AddRaftVoter = 80, + /// API key for request RemoveRaftVoterRequest + RemoveRaftVoter = 81, + /// API key for request UpdateRaftVoterRequest + UpdateRaftVoter = 82, } impl ApiKey { @@ -1220,6 +1262,9 @@ impl ApiKey { ApiKey::DescribeTopicPartitions => { DescribeTopicPartitionsRequest::header_version(version) } + ApiKey::AddRaftVoter => AddRaftVoterRequest::header_version(version), + ApiKey::RemoveRaftVoter => RemoveRaftVoterRequest::header_version(version), + ApiKey::UpdateRaftVoter => UpdateRaftVoterRequest::header_version(version), } } /// Get the version of response header that needs to be prepended to this message @@ -1323,14 +1368,17 @@ impl ApiKey { ApiKey::DescribeTopicPartitions => { DescribeTopicPartitionsResponse::header_version(version) } + ApiKey::AddRaftVoter => AddRaftVoterResponse::header_version(version), + ApiKey::RemoveRaftVoter => RemoveRaftVoterResponse::header_version(version), + ApiKey::UpdateRaftVoter => UpdateRaftVoterResponse::header_version(version), } } /// Returns the valid versions that can be used with this ApiKey pub fn valid_versions(&self) -> VersionRange { match self { ApiKey::Produce => VersionRange { min: 0, max: 11 }, - ApiKey::Fetch => VersionRange { min: 0, max: 16 }, - ApiKey::ListOffsets => VersionRange { min: 0, max: 8 }, + ApiKey::Fetch => VersionRange { min: 0, max: 17 }, + ApiKey::ListOffsets => VersionRange { min: 0, max: 9 }, ApiKey::Metadata => VersionRange { min: 0, max: 12 }, ApiKey::LeaderAndIsr => VersionRange { min: 0, max: 7 }, ApiKey::StopReplica => VersionRange { min: 0, max: 4 }, @@ -1338,7 +1386,7 @@ impl ApiKey { ApiKey::ControlledShutdown => VersionRange { min: 0, max: 3 }, ApiKey::OffsetCommit => VersionRange { min: 0, max: 9 }, ApiKey::OffsetFetch => VersionRange { min: 0, max: 9 }, - ApiKey::FindCoordinator => VersionRange { min: 0, max: 5 }, + ApiKey::FindCoordinator => VersionRange { min: 0, max: 6 }, ApiKey::JoinGroup => VersionRange { min: 0, max: 9 }, ApiKey::Heartbeat => VersionRange { min: 0, max: 4 }, ApiKey::LeaveGroup => VersionRange { min: 0, max: 5 }, @@ -1346,7 +1394,7 @@ impl ApiKey { ApiKey::DescribeGroups => VersionRange { min: 0, max: 5 }, ApiKey::ListGroups => VersionRange { min: 0, max: 5 }, ApiKey::SaslHandshake => VersionRange { min: 0, max: 1 }, - ApiKey::ApiVersions => VersionRange { min: 0, max: 3 }, + ApiKey::ApiVersions => VersionRange { min: 0, max: 4 }, ApiKey::CreateTopics => VersionRange { min: 0, max: 7 }, ApiKey::DeleteTopics => VersionRange { min: 0, max: 6 }, ApiKey::DeleteRecords => VersionRange { min: 0, max: 2 }, @@ -1380,17 +1428,17 @@ impl ApiKey { ApiKey::AlterClientQuotas => VersionRange { min: 0, max: 1 }, ApiKey::DescribeUserScramCredentials => VersionRange { min: 0, max: 0 }, ApiKey::AlterUserScramCredentials => VersionRange { min: 0, max: 0 }, - ApiKey::Vote => VersionRange { min: 0, max: 0 }, - ApiKey::BeginQuorumEpoch => VersionRange { min: 0, max: 0 }, - ApiKey::EndQuorumEpoch => VersionRange { min: 0, max: 0 }, - ApiKey::DescribeQuorum => VersionRange { min: 0, max: 1 }, + ApiKey::Vote => VersionRange { min: 0, max: 1 }, + ApiKey::BeginQuorumEpoch => VersionRange { min: 0, max: 1 }, + ApiKey::EndQuorumEpoch => VersionRange { min: 0, max: 1 }, + ApiKey::DescribeQuorum => VersionRange { min: 0, max: 2 }, ApiKey::AlterPartition => VersionRange { min: 0, max: 3 }, ApiKey::UpdateFeatures => VersionRange { min: 0, max: 1 }, ApiKey::Envelope => VersionRange { min: 0, max: 0 }, - ApiKey::FetchSnapshot => VersionRange { min: 0, max: 0 }, + ApiKey::FetchSnapshot => VersionRange { min: 0, max: 1 }, ApiKey::DescribeCluster => VersionRange { min: 0, max: 1 }, ApiKey::DescribeProducers => VersionRange { min: 0, max: 0 }, - ApiKey::BrokerRegistration => VersionRange { min: 0, max: 3 }, + ApiKey::BrokerRegistration => VersionRange { min: 0, max: 4 }, ApiKey::BrokerHeartbeat => VersionRange { min: 0, max: 1 }, ApiKey::UnregisterBroker => VersionRange { min: 0, max: 0 }, ApiKey::DescribeTransactions => VersionRange { min: 0, max: 0 }, @@ -1404,6 +1452,9 @@ impl ApiKey { ApiKey::AssignReplicasToDirs => VersionRange { min: 0, max: 0 }, ApiKey::ListClientMetricsResources => VersionRange { min: 0, max: 0 }, ApiKey::DescribeTopicPartitions => VersionRange { min: 0, max: 0 }, + ApiKey::AddRaftVoter => VersionRange { min: 0, max: 0 }, + ApiKey::RemoveRaftVoter => VersionRange { min: 0, max: 0 }, + ApiKey::UpdateRaftVoter => VersionRange { min: 0, max: 0 }, } } @@ -1505,6 +1556,9 @@ impl TryFrom for ApiKey { Ok(ApiKey::ListClientMetricsResources) } x if x == ApiKey::DescribeTopicPartitions as i16 => Ok(ApiKey::DescribeTopicPartitions), + x if x == ApiKey::AddRaftVoter as i16 => Ok(ApiKey::AddRaftVoter), + x if x == ApiKey::RemoveRaftVoter as i16 => Ok(ApiKey::RemoveRaftVoter), + x if x == ApiKey::UpdateRaftVoter as i16 => Ok(ApiKey::UpdateRaftVoter), _ => Err(()), } } @@ -1667,6 +1721,12 @@ pub enum RequestKind { ListClientMetricsResources(ListClientMetricsResourcesRequest), /// DescribeTopicPartitionsRequest, DescribeTopicPartitions(DescribeTopicPartitionsRequest), + /// AddRaftVoterRequest, + AddRaftVoter(AddRaftVoterRequest), + /// RemoveRaftVoterRequest, + RemoveRaftVoter(RemoveRaftVoterRequest), + /// UpdateRaftVoterRequest, + UpdateRaftVoter(UpdateRaftVoterRequest), } #[cfg(feature = "messages_enums")] @@ -1751,6 +1811,9 @@ impl RequestKind { RequestKind::AssignReplicasToDirs(x) => encode(x, bytes, version), RequestKind::ListClientMetricsResources(x) => encode(x, bytes, version), RequestKind::DescribeTopicPartitions(x) => encode(x, bytes, version), + RequestKind::AddRaftVoter(x) => encode(x, bytes, version), + RequestKind::RemoveRaftVoter(x) => encode(x, bytes, version), + RequestKind::UpdateRaftVoter(x) => encode(x, bytes, version), } } /// Decode the message from the provided buffer and version @@ -1889,6 +1952,9 @@ impl RequestKind { ApiKey::DescribeTopicPartitions => Ok(RequestKind::DescribeTopicPartitions(decode( bytes, version, )?)), + ApiKey::AddRaftVoter => Ok(RequestKind::AddRaftVoter(decode(bytes, version)?)), + ApiKey::RemoveRaftVoter => Ok(RequestKind::RemoveRaftVoter(decode(bytes, version)?)), + ApiKey::UpdateRaftVoter => Ok(RequestKind::UpdateRaftVoter(decode(bytes, version)?)), } } } @@ -2424,6 +2490,27 @@ impl From for RequestKind { } } +#[cfg(feature = "messages_enums")] +impl From for RequestKind { + fn from(value: AddRaftVoterRequest) -> RequestKind { + RequestKind::AddRaftVoter(value) + } +} + +#[cfg(feature = "messages_enums")] +impl From for RequestKind { + fn from(value: RemoveRaftVoterRequest) -> RequestKind { + RequestKind::RemoveRaftVoter(value) + } +} + +#[cfg(feature = "messages_enums")] +impl From for RequestKind { + fn from(value: UpdateRaftVoterRequest) -> RequestKind { + RequestKind::UpdateRaftVoter(value) + } +} + #[cfg(feature = "messages_enums")] #[cfg(any(feature = "client", feature = "broker"))] fn decode(bytes: &mut bytes::Bytes, version: i16) -> Result { @@ -2605,6 +2692,12 @@ pub enum ResponseKind { ListClientMetricsResources(ListClientMetricsResourcesResponse), /// DescribeTopicPartitionsResponse, DescribeTopicPartitions(DescribeTopicPartitionsResponse), + /// AddRaftVoterResponse, + AddRaftVoter(AddRaftVoterResponse), + /// RemoveRaftVoterResponse, + RemoveRaftVoter(RemoveRaftVoterResponse), + /// UpdateRaftVoterResponse, + UpdateRaftVoter(UpdateRaftVoterResponse), } #[cfg(feature = "messages_enums")] @@ -2689,6 +2782,9 @@ impl ResponseKind { ResponseKind::AssignReplicasToDirs(x) => encode(x, bytes, version), ResponseKind::ListClientMetricsResources(x) => encode(x, bytes, version), ResponseKind::DescribeTopicPartitions(x) => encode(x, bytes, version), + ResponseKind::AddRaftVoter(x) => encode(x, bytes, version), + ResponseKind::RemoveRaftVoter(x) => encode(x, bytes, version), + ResponseKind::UpdateRaftVoter(x) => encode(x, bytes, version), } } /// Decode the message from the provided buffer and version @@ -2827,6 +2923,9 @@ impl ResponseKind { ApiKey::DescribeTopicPartitions => Ok(ResponseKind::DescribeTopicPartitions(decode( bytes, version, )?)), + ApiKey::AddRaftVoter => Ok(ResponseKind::AddRaftVoter(decode(bytes, version)?)), + ApiKey::RemoveRaftVoter => Ok(ResponseKind::RemoveRaftVoter(decode(bytes, version)?)), + ApiKey::UpdateRaftVoter => Ok(ResponseKind::UpdateRaftVoter(decode(bytes, version)?)), } } /// Get the version of request header that needs to be prepended to this message @@ -2960,6 +3059,9 @@ impl ResponseKind { ResponseKind::DescribeTopicPartitions(_) => { DescribeTopicPartitionsResponse::header_version(version) } + ResponseKind::AddRaftVoter(_) => AddRaftVoterResponse::header_version(version), + ResponseKind::RemoveRaftVoter(_) => RemoveRaftVoterResponse::header_version(version), + ResponseKind::UpdateRaftVoter(_) => UpdateRaftVoterResponse::header_version(version), } } } @@ -3496,7 +3598,28 @@ impl From for ResponseKind { } } -/// The ID of the leader broker. +#[cfg(feature = "messages_enums")] +impl From for ResponseKind { + fn from(value: AddRaftVoterResponse) -> ResponseKind { + ResponseKind::AddRaftVoter(value) + } +} + +#[cfg(feature = "messages_enums")] +impl From for ResponseKind { + fn from(value: RemoveRaftVoterResponse) -> ResponseKind { + ResponseKind::RemoveRaftVoter(value) + } +} + +#[cfg(feature = "messages_enums")] +impl From for ResponseKind { + fn from(value: UpdateRaftVoterResponse) -> ResponseKind { + ResponseKind::UpdateRaftVoter(value) + } +} + +/// The replica id of the current leader or -1 if the leader is unknown #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default, Copy)] pub struct BrokerId(pub i32); diff --git a/src/messages/add_raft_voter_request.rs b/src/messages/add_raft_voter_request.rs new file mode 100644 index 0000000..d1729a9 --- /dev/null +++ b/src/messages/add_raft_voter_request.rs @@ -0,0 +1,341 @@ +//! AddRaftVoterRequest +//! +//! See the schema for this message [here](https://github.com/apache/kafka/blob/trunk/clients/src/main/resources/common/message/AddRaftVoterRequest.json). +// WARNING: the items of this module are generated and should not be edited directly +#![allow(unused)] + +use std::borrow::Borrow; +use std::collections::BTreeMap; + +use anyhow::{bail, Result}; +use bytes::Bytes; +use uuid::Uuid; + +use crate::protocol::{ + buf::{ByteBuf, ByteBufMut}, + compute_unknown_tagged_fields_size, types, write_unknown_tagged_fields, Decodable, Decoder, + Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, +}; + +/// Valid versions: 0 +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct AddRaftVoterRequest { + /// + /// + /// Supported API versions: 0 + pub cluster_id: Option, + + /// + /// + /// Supported API versions: 0 + pub timeout_ms: i32, + + /// The replica id of the voter getting added to the topic partition + /// + /// Supported API versions: 0 + pub voter_id: i32, + + /// The directory id of the voter getting added to the topic partition + /// + /// Supported API versions: 0 + pub voter_directory_id: Uuid, + + /// The endpoints that can be used to communicate with the voter + /// + /// Supported API versions: 0 + pub listeners: Vec, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, +} + +impl AddRaftVoterRequest { + /// Sets `cluster_id` to the passed value. + /// + /// + /// + /// Supported API versions: 0 + pub fn with_cluster_id(mut self, value: Option) -> Self { + self.cluster_id = value; + self + } + /// Sets `timeout_ms` to the passed value. + /// + /// + /// + /// Supported API versions: 0 + pub fn with_timeout_ms(mut self, value: i32) -> Self { + self.timeout_ms = value; + self + } + /// Sets `voter_id` to the passed value. + /// + /// The replica id of the voter getting added to the topic partition + /// + /// Supported API versions: 0 + pub fn with_voter_id(mut self, value: i32) -> Self { + self.voter_id = value; + self + } + /// Sets `voter_directory_id` to the passed value. + /// + /// The directory id of the voter getting added to the topic partition + /// + /// Supported API versions: 0 + pub fn with_voter_directory_id(mut self, value: Uuid) -> Self { + self.voter_directory_id = value; + self + } + /// Sets `listeners` to the passed value. + /// + /// The endpoints that can be used to communicate with the voter + /// + /// Supported API versions: 0 + pub fn with_listeners(mut self, value: Vec) -> Self { + self.listeners = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } +} + +#[cfg(feature = "client")] +impl Encodable for AddRaftVoterRequest { + fn encode(&self, buf: &mut B, version: i16) -> Result<()> { + types::CompactString.encode(buf, &self.cluster_id)?; + types::Int32.encode(buf, &self.timeout_ms)?; + types::Int32.encode(buf, &self.voter_id)?; + types::Uuid.encode(buf, &self.voter_directory_id)?; + types::CompactArray(types::Struct { version }).encode(buf, &self.listeners)?; + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + Ok(()) + } + fn compute_size(&self, version: i16) -> Result { + let mut total_size = 0; + total_size += types::CompactString.compute_size(&self.cluster_id)?; + total_size += types::Int32.compute_size(&self.timeout_ms)?; + total_size += types::Int32.compute_size(&self.voter_id)?; + total_size += types::Uuid.compute_size(&self.voter_directory_id)?; + total_size += + types::CompactArray(types::Struct { version }).compute_size(&self.listeners)?; + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + Ok(total_size) + } +} + +#[cfg(feature = "broker")] +impl Decodable for AddRaftVoterRequest { + fn decode(buf: &mut B, version: i16) -> Result { + let cluster_id = types::CompactString.decode(buf)?; + let timeout_ms = types::Int32.decode(buf)?; + let voter_id = types::Int32.decode(buf)?; + let voter_directory_id = types::Uuid.decode(buf)?; + let listeners = types::CompactArray(types::Struct { version }).decode(buf)?; + let mut unknown_tagged_fields = BTreeMap::new(); + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + Ok(Self { + cluster_id, + timeout_ms, + voter_id, + voter_directory_id, + listeners, + unknown_tagged_fields, + }) + } +} + +impl Default for AddRaftVoterRequest { + fn default() -> Self { + Self { + cluster_id: Some(Default::default()), + timeout_ms: 0, + voter_id: 0, + voter_directory_id: Uuid::nil(), + listeners: Default::default(), + unknown_tagged_fields: BTreeMap::new(), + } + } +} + +impl Message for AddRaftVoterRequest { + const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const DEPRECATED_VERSIONS: Option = None; +} + +/// Valid versions: 0 +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct Listener { + /// The name of the endpoint + /// + /// Supported API versions: 0 + pub name: StrBytes, + + /// The hostname + /// + /// Supported API versions: 0 + pub host: StrBytes, + + /// The port + /// + /// Supported API versions: 0 + pub port: u16, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, +} + +impl Listener { + /// Sets `name` to the passed value. + /// + /// The name of the endpoint + /// + /// Supported API versions: 0 + pub fn with_name(mut self, value: StrBytes) -> Self { + self.name = value; + self + } + /// Sets `host` to the passed value. + /// + /// The hostname + /// + /// Supported API versions: 0 + pub fn with_host(mut self, value: StrBytes) -> Self { + self.host = value; + self + } + /// Sets `port` to the passed value. + /// + /// The port + /// + /// Supported API versions: 0 + pub fn with_port(mut self, value: u16) -> Self { + self.port = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } +} + +#[cfg(feature = "client")] +impl Encodable for Listener { + fn encode(&self, buf: &mut B, version: i16) -> Result<()> { + types::CompactString.encode(buf, &self.name)?; + types::CompactString.encode(buf, &self.host)?; + types::UInt16.encode(buf, &self.port)?; + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + Ok(()) + } + fn compute_size(&self, version: i16) -> Result { + let mut total_size = 0; + total_size += types::CompactString.compute_size(&self.name)?; + total_size += types::CompactString.compute_size(&self.host)?; + total_size += types::UInt16.compute_size(&self.port)?; + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + Ok(total_size) + } +} + +#[cfg(feature = "broker")] +impl Decodable for Listener { + fn decode(buf: &mut B, version: i16) -> Result { + let name = types::CompactString.decode(buf)?; + let host = types::CompactString.decode(buf)?; + let port = types::UInt16.decode(buf)?; + let mut unknown_tagged_fields = BTreeMap::new(); + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + Ok(Self { + name, + host, + port, + unknown_tagged_fields, + }) + } +} + +impl Default for Listener { + fn default() -> Self { + Self { + name: Default::default(), + host: Default::default(), + port: 0, + unknown_tagged_fields: BTreeMap::new(), + } + } +} + +impl Message for Listener { + const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const DEPRECATED_VERSIONS: Option = None; +} + +impl HeaderVersion for AddRaftVoterRequest { + fn header_version(version: i16) -> i16 { + 2 + } +} diff --git a/src/messages/add_raft_voter_response.rs b/src/messages/add_raft_voter_response.rs new file mode 100644 index 0000000..4f84399 --- /dev/null +++ b/src/messages/add_raft_voter_response.rs @@ -0,0 +1,163 @@ +//! AddRaftVoterResponse +//! +//! See the schema for this message [here](https://github.com/apache/kafka/blob/trunk/clients/src/main/resources/common/message/AddRaftVoterResponse.json). +// WARNING: the items of this module are generated and should not be edited directly +#![allow(unused)] + +use std::borrow::Borrow; +use std::collections::BTreeMap; + +use anyhow::{bail, Result}; +use bytes::Bytes; +use uuid::Uuid; + +use crate::protocol::{ + buf::{ByteBuf, ByteBufMut}, + compute_unknown_tagged_fields_size, types, write_unknown_tagged_fields, Decodable, Decoder, + Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, +}; + +/// Valid versions: 0 +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct AddRaftVoterResponse { + /// The duration in milliseconds for which the request was throttled due to a quota violation, or zero if the request did not violate any quota. + /// + /// Supported API versions: 0 + pub throttle_time_ms: i32, + + /// The error code, or 0 if there was no error + /// + /// Supported API versions: 0 + pub error_code: i16, + + /// The error message, or null if there was no error. + /// + /// Supported API versions: 0 + pub error_message: Option, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, +} + +impl AddRaftVoterResponse { + /// Sets `throttle_time_ms` to the passed value. + /// + /// The duration in milliseconds for which the request was throttled due to a quota violation, or zero if the request did not violate any quota. + /// + /// Supported API versions: 0 + pub fn with_throttle_time_ms(mut self, value: i32) -> Self { + self.throttle_time_ms = value; + self + } + /// Sets `error_code` to the passed value. + /// + /// The error code, or 0 if there was no error + /// + /// Supported API versions: 0 + pub fn with_error_code(mut self, value: i16) -> Self { + self.error_code = value; + self + } + /// Sets `error_message` to the passed value. + /// + /// The error message, or null if there was no error. + /// + /// Supported API versions: 0 + pub fn with_error_message(mut self, value: Option) -> Self { + self.error_message = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } +} + +#[cfg(feature = "broker")] +impl Encodable for AddRaftVoterResponse { + fn encode(&self, buf: &mut B, version: i16) -> Result<()> { + types::Int32.encode(buf, &self.throttle_time_ms)?; + types::Int16.encode(buf, &self.error_code)?; + types::CompactString.encode(buf, &self.error_message)?; + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + Ok(()) + } + fn compute_size(&self, version: i16) -> Result { + let mut total_size = 0; + total_size += types::Int32.compute_size(&self.throttle_time_ms)?; + total_size += types::Int16.compute_size(&self.error_code)?; + total_size += types::CompactString.compute_size(&self.error_message)?; + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + Ok(total_size) + } +} + +#[cfg(feature = "client")] +impl Decodable for AddRaftVoterResponse { + fn decode(buf: &mut B, version: i16) -> Result { + let throttle_time_ms = types::Int32.decode(buf)?; + let error_code = types::Int16.decode(buf)?; + let error_message = types::CompactString.decode(buf)?; + let mut unknown_tagged_fields = BTreeMap::new(); + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + Ok(Self { + throttle_time_ms, + error_code, + error_message, + unknown_tagged_fields, + }) + } +} + +impl Default for AddRaftVoterResponse { + fn default() -> Self { + Self { + throttle_time_ms: 0, + error_code: 0, + error_message: Some(Default::default()), + unknown_tagged_fields: BTreeMap::new(), + } + } +} + +impl Message for AddRaftVoterResponse { + const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const DEPRECATED_VERSIONS: Option = None; +} + +impl HeaderVersion for AddRaftVoterResponse { + fn header_version(version: i16) -> i16 { + 1 + } +} diff --git a/src/messages/api_versions_request.rs b/src/messages/api_versions_request.rs index 34f55f5..f2cb89e 100644 --- a/src/messages/api_versions_request.rs +++ b/src/messages/api_versions_request.rs @@ -17,18 +17,18 @@ use crate::protocol::{ Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, }; -/// Valid versions: 0-3 +/// Valid versions: 0-4 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct ApiVersionsRequest { /// The name of the client. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub client_software_name: StrBytes, /// The version of the client. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub client_software_version: StrBytes, /// Other tagged fields @@ -40,7 +40,7 @@ impl ApiVersionsRequest { /// /// The name of the client. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub fn with_client_software_name(mut self, value: StrBytes) -> Self { self.client_software_name = value; self @@ -49,7 +49,7 @@ impl ApiVersionsRequest { /// /// The version of the client. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub fn with_client_software_version(mut self, value: StrBytes) -> Self { self.client_software_version = value; self @@ -155,7 +155,7 @@ impl Default for ApiVersionsRequest { } impl Message for ApiVersionsRequest { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 3 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 4 }; const DEPRECATED_VERSIONS: Option = None; } diff --git a/src/messages/api_versions_response.rs b/src/messages/api_versions_response.rs index 245f4d9..94350b9 100644 --- a/src/messages/api_versions_response.rs +++ b/src/messages/api_versions_response.rs @@ -17,23 +17,23 @@ use crate::protocol::{ Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, }; -/// Valid versions: 0-3 +/// Valid versions: 0-4 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct ApiVersion { /// The API index. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub api_key: i16, /// The minimum supported version, inclusive. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub min_version: i16, /// The maximum supported version, inclusive. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub max_version: i16, /// Other tagged fields @@ -45,7 +45,7 @@ impl ApiVersion { /// /// The API index. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub fn with_api_key(mut self, value: i16) -> Self { self.api_key = value; self @@ -54,7 +54,7 @@ impl ApiVersion { /// /// The minimum supported version, inclusive. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub fn with_min_version(mut self, value: i16) -> Self { self.min_version = value; self @@ -63,7 +63,7 @@ impl ApiVersion { /// /// The maximum supported version, inclusive. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub fn with_max_version(mut self, value: i16) -> Self { self.max_version = value; self @@ -158,47 +158,47 @@ impl Default for ApiVersion { } impl Message for ApiVersion { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 3 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 4 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0-3 +/// Valid versions: 0-4 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct ApiVersionsResponse { /// The top-level error code. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub error_code: i16, /// The APIs supported by the broker. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub api_keys: Vec, /// The duration in milliseconds for which the request was throttled due to a quota violation, or zero if the request did not violate any quota. /// - /// Supported API versions: 1-3 + /// Supported API versions: 1-4 pub throttle_time_ms: i32, - /// Features supported by the broker. + /// Features supported by the broker. Note: in v0-v3, features with MinSupportedVersion = 0 are omitted. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub supported_features: Vec, /// The monotonically increasing epoch for the finalized features information. Valid values are >= 0. A value of -1 is special and represents unknown epoch. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub finalized_features_epoch: i64, /// List of cluster-wide finalized features. The information is valid only if FinalizedFeaturesEpoch >= 0. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub finalized_features: Vec, /// Set by a KRaft controller if the required configurations for ZK migration are present /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub zk_migration_ready: bool, /// Other tagged fields @@ -210,7 +210,7 @@ impl ApiVersionsResponse { /// /// The top-level error code. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub fn with_error_code(mut self, value: i16) -> Self { self.error_code = value; self @@ -219,7 +219,7 @@ impl ApiVersionsResponse { /// /// The APIs supported by the broker. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub fn with_api_keys(mut self, value: Vec) -> Self { self.api_keys = value; self @@ -228,16 +228,16 @@ impl ApiVersionsResponse { /// /// The duration in milliseconds for which the request was throttled due to a quota violation, or zero if the request did not violate any quota. /// - /// Supported API versions: 1-3 + /// Supported API versions: 1-4 pub fn with_throttle_time_ms(mut self, value: i32) -> Self { self.throttle_time_ms = value; self } /// Sets `supported_features` to the passed value. /// - /// Features supported by the broker. + /// Features supported by the broker. Note: in v0-v3, features with MinSupportedVersion = 0 are omitted. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub fn with_supported_features(mut self, value: Vec) -> Self { self.supported_features = value; self @@ -246,7 +246,7 @@ impl ApiVersionsResponse { /// /// The monotonically increasing epoch for the finalized features information. Valid values are >= 0. A value of -1 is special and represents unknown epoch. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub fn with_finalized_features_epoch(mut self, value: i64) -> Self { self.finalized_features_epoch = value; self @@ -255,7 +255,7 @@ impl ApiVersionsResponse { /// /// List of cluster-wide finalized features. The information is valid only if FinalizedFeaturesEpoch >= 0. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub fn with_finalized_features(mut self, value: Vec) -> Self { self.finalized_features = value; self @@ -264,7 +264,7 @@ impl ApiVersionsResponse { /// /// Set by a KRaft controller if the required configurations for ZK migration are present /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub fn with_zk_migration_ready(mut self, value: bool) -> Self { self.zk_migration_ready = value; self @@ -536,27 +536,27 @@ impl Default for ApiVersionsResponse { } impl Message for ApiVersionsResponse { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 3 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 4 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0-3 +/// Valid versions: 0-4 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct FinalizedFeatureKey { /// The name of the feature. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub name: StrBytes, /// The cluster-wide finalized max version level for the feature. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub max_version_level: i16, /// The cluster-wide finalized min version level for the feature. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub min_version_level: i16, /// Other tagged fields @@ -568,7 +568,7 @@ impl FinalizedFeatureKey { /// /// The name of the feature. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub fn with_name(mut self, value: StrBytes) -> Self { self.name = value; self @@ -577,7 +577,7 @@ impl FinalizedFeatureKey { /// /// The cluster-wide finalized max version level for the feature. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub fn with_max_version_level(mut self, value: i16) -> Self { self.max_version_level = value; self @@ -586,7 +586,7 @@ impl FinalizedFeatureKey { /// /// The cluster-wide finalized min version level for the feature. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub fn with_min_version_level(mut self, value: i16) -> Self { self.min_version_level = value; self @@ -729,27 +729,27 @@ impl Default for FinalizedFeatureKey { } impl Message for FinalizedFeatureKey { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 3 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 4 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0-3 +/// Valid versions: 0-4 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct SupportedFeatureKey { /// The name of the feature. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub name: StrBytes, /// The minimum supported version for the feature. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub min_version: i16, /// The maximum supported version for the feature. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub max_version: i16, /// Other tagged fields @@ -761,7 +761,7 @@ impl SupportedFeatureKey { /// /// The name of the feature. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub fn with_name(mut self, value: StrBytes) -> Self { self.name = value; self @@ -770,7 +770,7 @@ impl SupportedFeatureKey { /// /// The minimum supported version for the feature. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub fn with_min_version(mut self, value: i16) -> Self { self.min_version = value; self @@ -779,7 +779,7 @@ impl SupportedFeatureKey { /// /// The maximum supported version for the feature. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub fn with_max_version(mut self, value: i16) -> Self { self.max_version = value; self @@ -922,7 +922,7 @@ impl Default for SupportedFeatureKey { } impl Message for SupportedFeatureKey { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 3 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 4 }; const DEPRECATED_VERSIONS: Option = None; } diff --git a/src/messages/begin_quorum_epoch_request.rs b/src/messages/begin_quorum_epoch_request.rs index 4464bb0..72b93fe 100644 --- a/src/messages/begin_quorum_epoch_request.rs +++ b/src/messages/begin_quorum_epoch_request.rs @@ -17,19 +17,32 @@ use crate::protocol::{ Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, }; -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct BeginQuorumEpochRequest { /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub cluster_id: Option, + /// The replica id of the voter receiving the request + /// + /// Supported API versions: 1 + pub voter_id: super::BrokerId, + /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub topics: Vec, + + /// Endpoints for the leader + /// + /// Supported API versions: 1 + pub leader_endpoints: Vec, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, } impl BeginQuorumEpochRequest { @@ -37,35 +50,115 @@ impl BeginQuorumEpochRequest { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_cluster_id(mut self, value: Option) -> Self { self.cluster_id = value; self } + /// Sets `voter_id` to the passed value. + /// + /// The replica id of the voter receiving the request + /// + /// Supported API versions: 1 + pub fn with_voter_id(mut self, value: super::BrokerId) -> Self { + self.voter_id = value; + self + } /// Sets `topics` to the passed value. /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_topics(mut self, value: Vec) -> Self { self.topics = value; self } + /// Sets `leader_endpoints` to the passed value. + /// + /// Endpoints for the leader + /// + /// Supported API versions: 1 + pub fn with_leader_endpoints(mut self, value: Vec) -> Self { + self.leader_endpoints = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } } #[cfg(feature = "client")] impl Encodable for BeginQuorumEpochRequest { fn encode(&self, buf: &mut B, version: i16) -> Result<()> { - types::String.encode(buf, &self.cluster_id)?; - types::Array(types::Struct { version }).encode(buf, &self.topics)?; - + if version >= 1 { + types::CompactString.encode(buf, &self.cluster_id)?; + } else { + types::String.encode(buf, &self.cluster_id)?; + } + if version >= 1 { + types::Int32.encode(buf, &self.voter_id)?; + } + if version >= 1 { + types::CompactArray(types::Struct { version }).encode(buf, &self.topics)?; + } else { + types::Array(types::Struct { version }).encode(buf, &self.topics)?; + } + if version >= 1 { + types::CompactArray(types::Struct { version }).encode(buf, &self.leader_endpoints)?; + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + } Ok(()) } fn compute_size(&self, version: i16) -> Result { let mut total_size = 0; - total_size += types::String.compute_size(&self.cluster_id)?; - total_size += types::Array(types::Struct { version }).compute_size(&self.topics)?; - + if version >= 1 { + total_size += types::CompactString.compute_size(&self.cluster_id)?; + } else { + total_size += types::String.compute_size(&self.cluster_id)?; + } + if version >= 1 { + total_size += types::Int32.compute_size(&self.voter_id)?; + } + if version >= 1 { + total_size += + types::CompactArray(types::Struct { version }).compute_size(&self.topics)?; + } else { + total_size += types::Array(types::Struct { version }).compute_size(&self.topics)?; + } + if version >= 1 { + total_size += types::CompactArray(types::Struct { version }) + .compute_size(&self.leader_endpoints)?; + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + } Ok(total_size) } } @@ -73,9 +166,43 @@ impl Encodable for BeginQuorumEpochRequest { #[cfg(feature = "broker")] impl Decodable for BeginQuorumEpochRequest { fn decode(buf: &mut B, version: i16) -> Result { - let cluster_id = types::String.decode(buf)?; - let topics = types::Array(types::Struct { version }).decode(buf)?; - Ok(Self { cluster_id, topics }) + let cluster_id = if version >= 1 { + types::CompactString.decode(buf)? + } else { + types::String.decode(buf)? + }; + let voter_id = if version >= 1 { + types::Int32.decode(buf)? + } else { + (-1).into() + }; + let topics = if version >= 1 { + types::CompactArray(types::Struct { version }).decode(buf)? + } else { + types::Array(types::Struct { version }).decode(buf)? + }; + let leader_endpoints = if version >= 1 { + types::CompactArray(types::Struct { version }).decode(buf)? + } else { + Default::default() + }; + let mut unknown_tagged_fields = BTreeMap::new(); + if version >= 1 { + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } + Ok(Self { + cluster_id, + voter_id, + topics, + leader_endpoints, + unknown_tagged_fields, + }) } } @@ -83,51 +210,264 @@ impl Default for BeginQuorumEpochRequest { fn default() -> Self { Self { cluster_id: None, + voter_id: (-1).into(), topics: Default::default(), + leader_endpoints: Default::default(), + unknown_tagged_fields: BTreeMap::new(), } } } impl Message for BeginQuorumEpochRequest { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; + const DEPRECATED_VERSIONS: Option = None; +} + +/// Valid versions: 0-1 +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct LeaderEndpoint { + /// The name of the endpoint + /// + /// Supported API versions: 1 + pub name: StrBytes, + + /// The node's hostname + /// + /// Supported API versions: 1 + pub host: StrBytes, + + /// The node's port + /// + /// Supported API versions: 1 + pub port: u16, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, +} + +impl LeaderEndpoint { + /// Sets `name` to the passed value. + /// + /// The name of the endpoint + /// + /// Supported API versions: 1 + pub fn with_name(mut self, value: StrBytes) -> Self { + self.name = value; + self + } + /// Sets `host` to the passed value. + /// + /// The node's hostname + /// + /// Supported API versions: 1 + pub fn with_host(mut self, value: StrBytes) -> Self { + self.host = value; + self + } + /// Sets `port` to the passed value. + /// + /// The node's port + /// + /// Supported API versions: 1 + pub fn with_port(mut self, value: u16) -> Self { + self.port = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } +} + +#[cfg(feature = "client")] +impl Encodable for LeaderEndpoint { + fn encode(&self, buf: &mut B, version: i16) -> Result<()> { + if version >= 1 { + types::CompactString.encode(buf, &self.name)?; + } else { + if !self.name.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + types::CompactString.encode(buf, &self.host)?; + } else { + if !self.host.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + types::UInt16.encode(buf, &self.port)?; + } else { + if self.port != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + } + Ok(()) + } + fn compute_size(&self, version: i16) -> Result { + let mut total_size = 0; + if version >= 1 { + total_size += types::CompactString.compute_size(&self.name)?; + } else { + if !self.name.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + total_size += types::CompactString.compute_size(&self.host)?; + } else { + if !self.host.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + total_size += types::UInt16.compute_size(&self.port)?; + } else { + if self.port != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + } + Ok(total_size) + } +} + +#[cfg(feature = "broker")] +impl Decodable for LeaderEndpoint { + fn decode(buf: &mut B, version: i16) -> Result { + let name = if version >= 1 { + types::CompactString.decode(buf)? + } else { + Default::default() + }; + let host = if version >= 1 { + types::CompactString.decode(buf)? + } else { + Default::default() + }; + let port = if version >= 1 { + types::UInt16.decode(buf)? + } else { + 0 + }; + let mut unknown_tagged_fields = BTreeMap::new(); + if version >= 1 { + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } + Ok(Self { + name, + host, + port, + unknown_tagged_fields, + }) + } +} + +impl Default for LeaderEndpoint { + fn default() -> Self { + Self { + name: Default::default(), + host: Default::default(), + port: 0, + unknown_tagged_fields: BTreeMap::new(), + } + } +} + +impl Message for LeaderEndpoint { + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct PartitionData { - /// The partition index. + /// The partition index /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub partition_index: i32, + /// The directory id of the receiving replica + /// + /// Supported API versions: 1 + pub voter_directory_id: Uuid, + /// The ID of the newly elected leader /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub leader_id: super::BrokerId, /// The epoch of the newly elected leader /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub leader_epoch: i32, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, } impl PartitionData { /// Sets `partition_index` to the passed value. /// - /// The partition index. + /// The partition index /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_partition_index(mut self, value: i32) -> Self { self.partition_index = value; self } + /// Sets `voter_directory_id` to the passed value. + /// + /// The directory id of the receiving replica + /// + /// Supported API versions: 1 + pub fn with_voter_directory_id(mut self, value: Uuid) -> Self { + self.voter_directory_id = value; + self + } /// Sets `leader_id` to the passed value. /// /// The ID of the newly elected leader /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_leader_id(mut self, value: super::BrokerId) -> Self { self.leader_id = value; self @@ -136,28 +476,66 @@ impl PartitionData { /// /// The epoch of the newly elected leader /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_leader_epoch(mut self, value: i32) -> Self { self.leader_epoch = value; self } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } } #[cfg(feature = "client")] impl Encodable for PartitionData { fn encode(&self, buf: &mut B, version: i16) -> Result<()> { types::Int32.encode(buf, &self.partition_index)?; + if version >= 1 { + types::Uuid.encode(buf, &self.voter_directory_id)?; + } types::Int32.encode(buf, &self.leader_id)?; types::Int32.encode(buf, &self.leader_epoch)?; - + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + } Ok(()) } fn compute_size(&self, version: i16) -> Result { let mut total_size = 0; total_size += types::Int32.compute_size(&self.partition_index)?; + if version >= 1 { + total_size += types::Uuid.compute_size(&self.voter_directory_id)?; + } total_size += types::Int32.compute_size(&self.leader_id)?; total_size += types::Int32.compute_size(&self.leader_epoch)?; - + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + } Ok(total_size) } } @@ -166,12 +544,29 @@ impl Encodable for PartitionData { impl Decodable for PartitionData { fn decode(buf: &mut B, version: i16) -> Result { let partition_index = types::Int32.decode(buf)?; + let voter_directory_id = if version >= 1 { + types::Uuid.decode(buf)? + } else { + Uuid::nil() + }; let leader_id = types::Int32.decode(buf)?; let leader_epoch = types::Int32.decode(buf)?; + let mut unknown_tagged_fields = BTreeMap::new(); + if version >= 1 { + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } Ok(Self { partition_index, + voter_directory_id, leader_id, leader_epoch, + unknown_tagged_fields, }) } } @@ -180,38 +575,43 @@ impl Default for PartitionData { fn default() -> Self { Self { partition_index: 0, + voter_directory_id: Uuid::nil(), leader_id: (0).into(), leader_epoch: 0, + unknown_tagged_fields: BTreeMap::new(), } } } impl Message for PartitionData { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct TopicData { - /// The topic name. + /// The topic name /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub topic_name: super::TopicName, /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub partitions: Vec, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, } impl TopicData { /// Sets `topic_name` to the passed value. /// - /// The topic name. + /// The topic name /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_topic_name(mut self, value: super::TopicName) -> Self { self.topic_name = value; self @@ -220,26 +620,75 @@ impl TopicData { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_partitions(mut self, value: Vec) -> Self { self.partitions = value; self } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } } #[cfg(feature = "client")] impl Encodable for TopicData { fn encode(&self, buf: &mut B, version: i16) -> Result<()> { - types::String.encode(buf, &self.topic_name)?; - types::Array(types::Struct { version }).encode(buf, &self.partitions)?; - + if version >= 1 { + types::CompactString.encode(buf, &self.topic_name)?; + } else { + types::String.encode(buf, &self.topic_name)?; + } + if version >= 1 { + types::CompactArray(types::Struct { version }).encode(buf, &self.partitions)?; + } else { + types::Array(types::Struct { version }).encode(buf, &self.partitions)?; + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + } Ok(()) } fn compute_size(&self, version: i16) -> Result { let mut total_size = 0; - total_size += types::String.compute_size(&self.topic_name)?; - total_size += types::Array(types::Struct { version }).compute_size(&self.partitions)?; - + if version >= 1 { + total_size += types::CompactString.compute_size(&self.topic_name)?; + } else { + total_size += types::String.compute_size(&self.topic_name)?; + } + if version >= 1 { + total_size += + types::CompactArray(types::Struct { version }).compute_size(&self.partitions)?; + } else { + total_size += types::Array(types::Struct { version }).compute_size(&self.partitions)?; + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + } Ok(total_size) } } @@ -247,11 +696,30 @@ impl Encodable for TopicData { #[cfg(feature = "broker")] impl Decodable for TopicData { fn decode(buf: &mut B, version: i16) -> Result { - let topic_name = types::String.decode(buf)?; - let partitions = types::Array(types::Struct { version }).decode(buf)?; + let topic_name = if version >= 1 { + types::CompactString.decode(buf)? + } else { + types::String.decode(buf)? + }; + let partitions = if version >= 1 { + types::CompactArray(types::Struct { version }).decode(buf)? + } else { + types::Array(types::Struct { version }).decode(buf)? + }; + let mut unknown_tagged_fields = BTreeMap::new(); + if version >= 1 { + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } Ok(Self { topic_name, partitions, + unknown_tagged_fields, }) } } @@ -261,17 +729,22 @@ impl Default for TopicData { Self { topic_name: Default::default(), partitions: Default::default(), + unknown_tagged_fields: BTreeMap::new(), } } } impl Message for TopicData { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } impl HeaderVersion for BeginQuorumEpochRequest { fn header_version(version: i16) -> i16 { - 1 + if version >= 1 { + 2 + } else { + 1 + } } } diff --git a/src/messages/begin_quorum_epoch_response.rs b/src/messages/begin_quorum_epoch_response.rs index 1cfbd9b..bcafebc 100644 --- a/src/messages/begin_quorum_epoch_response.rs +++ b/src/messages/begin_quorum_epoch_response.rs @@ -17,19 +17,27 @@ use crate::protocol::{ Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, }; -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct BeginQuorumEpochResponse { /// The top level error code. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub error_code: i16, /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub topics: Vec, + + /// Endpoints for all leaders enumerated in PartitionData + /// + /// Supported API versions: 1 + pub node_endpoints: Vec, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, } impl BeginQuorumEpochResponse { @@ -37,7 +45,7 @@ impl BeginQuorumEpochResponse { /// /// The top level error code. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_error_code(mut self, value: i16) -> Self { self.error_code = value; self @@ -46,26 +54,108 @@ impl BeginQuorumEpochResponse { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_topics(mut self, value: Vec) -> Self { self.topics = value; self } + /// Sets `node_endpoints` to the passed value. + /// + /// Endpoints for all leaders enumerated in PartitionData + /// + /// Supported API versions: 1 + pub fn with_node_endpoints(mut self, value: Vec) -> Self { + self.node_endpoints = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } } #[cfg(feature = "broker")] impl Encodable for BeginQuorumEpochResponse { fn encode(&self, buf: &mut B, version: i16) -> Result<()> { types::Int16.encode(buf, &self.error_code)?; - types::Array(types::Struct { version }).encode(buf, &self.topics)?; - + if version >= 1 { + types::CompactArray(types::Struct { version }).encode(buf, &self.topics)?; + } else { + types::Array(types::Struct { version }).encode(buf, &self.topics)?; + } + if version >= 1 { + let mut num_tagged_fields = self.unknown_tagged_fields.len(); + if !self.node_endpoints.is_empty() { + num_tagged_fields += 1; + } + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + if !self.node_endpoints.is_empty() { + let computed_size = types::CompactArray(types::Struct { version }) + .compute_size(&self.node_endpoints)?; + if computed_size > std::u32::MAX as usize { + bail!( + "Tagged field is too large to encode ({} bytes)", + computed_size + ); + } + types::UnsignedVarInt.encode(buf, 0)?; + types::UnsignedVarInt.encode(buf, computed_size as u32)?; + types::CompactArray(types::Struct { version }).encode(buf, &self.node_endpoints)?; + } + + write_unknown_tagged_fields(buf, 1.., &self.unknown_tagged_fields)?; + } Ok(()) } fn compute_size(&self, version: i16) -> Result { let mut total_size = 0; total_size += types::Int16.compute_size(&self.error_code)?; - total_size += types::Array(types::Struct { version }).compute_size(&self.topics)?; - + if version >= 1 { + total_size += + types::CompactArray(types::Struct { version }).compute_size(&self.topics)?; + } else { + total_size += types::Array(types::Struct { version }).compute_size(&self.topics)?; + } + if version >= 1 { + let mut num_tagged_fields = self.unknown_tagged_fields.len(); + if !self.node_endpoints.is_empty() { + num_tagged_fields += 1; + } + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + if !self.node_endpoints.is_empty() { + let computed_size = types::CompactArray(types::Struct { version }) + .compute_size(&self.node_endpoints)?; + if computed_size > std::u32::MAX as usize { + bail!( + "Tagged field is too large to encode ({} bytes)", + computed_size + ); + } + total_size += types::UnsignedVarInt.compute_size(0)?; + total_size += types::UnsignedVarInt.compute_size(computed_size as u32)?; + total_size += computed_size; + } + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + } Ok(total_size) } } @@ -74,8 +164,36 @@ impl Encodable for BeginQuorumEpochResponse { impl Decodable for BeginQuorumEpochResponse { fn decode(buf: &mut B, version: i16) -> Result { let error_code = types::Int16.decode(buf)?; - let topics = types::Array(types::Struct { version }).decode(buf)?; - Ok(Self { error_code, topics }) + let topics = if version >= 1 { + types::CompactArray(types::Struct { version }).decode(buf)? + } else { + types::Array(types::Struct { version }).decode(buf)? + }; + let mut node_endpoints = Default::default(); + let mut unknown_tagged_fields = BTreeMap::new(); + if version >= 1 { + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + match tag { + 0 => { + node_endpoints = + types::CompactArray(types::Struct { version }).decode(buf)?; + } + _ => { + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } + } + } + Ok(Self { + error_code, + topics, + node_endpoints, + unknown_tagged_fields, + }) } } @@ -84,38 +202,236 @@ impl Default for BeginQuorumEpochResponse { Self { error_code: 0, topics: Default::default(), + node_endpoints: Default::default(), + unknown_tagged_fields: BTreeMap::new(), } } } impl Message for BeginQuorumEpochResponse { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0 +/// Valid versions: 0-1 +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct NodeEndpoint { + /// The ID of the associated node + /// + /// Supported API versions: 1 + pub node_id: super::BrokerId, + + /// The node's hostname + /// + /// Supported API versions: 1 + pub host: StrBytes, + + /// The node's port + /// + /// Supported API versions: 1 + pub port: u16, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, +} + +impl NodeEndpoint { + /// Sets `node_id` to the passed value. + /// + /// The ID of the associated node + /// + /// Supported API versions: 1 + pub fn with_node_id(mut self, value: super::BrokerId) -> Self { + self.node_id = value; + self + } + /// Sets `host` to the passed value. + /// + /// The node's hostname + /// + /// Supported API versions: 1 + pub fn with_host(mut self, value: StrBytes) -> Self { + self.host = value; + self + } + /// Sets `port` to the passed value. + /// + /// The node's port + /// + /// Supported API versions: 1 + pub fn with_port(mut self, value: u16) -> Self { + self.port = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } +} + +#[cfg(feature = "broker")] +impl Encodable for NodeEndpoint { + fn encode(&self, buf: &mut B, version: i16) -> Result<()> { + if version >= 1 { + types::Int32.encode(buf, &self.node_id)?; + } else { + if self.node_id != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + types::CompactString.encode(buf, &self.host)?; + } else { + if !self.host.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + types::UInt16.encode(buf, &self.port)?; + } else { + if self.port != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + } + Ok(()) + } + fn compute_size(&self, version: i16) -> Result { + let mut total_size = 0; + if version >= 1 { + total_size += types::Int32.compute_size(&self.node_id)?; + } else { + if self.node_id != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + total_size += types::CompactString.compute_size(&self.host)?; + } else { + if !self.host.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + total_size += types::UInt16.compute_size(&self.port)?; + } else { + if self.port != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + } + Ok(total_size) + } +} + +#[cfg(feature = "client")] +impl Decodable for NodeEndpoint { + fn decode(buf: &mut B, version: i16) -> Result { + let node_id = if version >= 1 { + types::Int32.decode(buf)? + } else { + (0).into() + }; + let host = if version >= 1 { + types::CompactString.decode(buf)? + } else { + Default::default() + }; + let port = if version >= 1 { + types::UInt16.decode(buf)? + } else { + 0 + }; + let mut unknown_tagged_fields = BTreeMap::new(); + if version >= 1 { + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } + Ok(Self { + node_id, + host, + port, + unknown_tagged_fields, + }) + } +} + +impl Default for NodeEndpoint { + fn default() -> Self { + Self { + node_id: (0).into(), + host: Default::default(), + port: 0, + unknown_tagged_fields: BTreeMap::new(), + } + } +} + +impl Message for NodeEndpoint { + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; + const DEPRECATED_VERSIONS: Option = None; +} + +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct PartitionData { /// The partition index. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub partition_index: i32, /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub error_code: i16, /// The ID of the current leader or -1 if the leader is unknown. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub leader_id: super::BrokerId, /// The latest known leader epoch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub leader_epoch: i32, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, } impl PartitionData { @@ -123,7 +439,7 @@ impl PartitionData { /// /// The partition index. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_partition_index(mut self, value: i32) -> Self { self.partition_index = value; self @@ -132,7 +448,7 @@ impl PartitionData { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_error_code(mut self, value: i16) -> Self { self.error_code = value; self @@ -141,7 +457,7 @@ impl PartitionData { /// /// The ID of the current leader or -1 if the leader is unknown. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_leader_id(mut self, value: super::BrokerId) -> Self { self.leader_id = value; self @@ -150,11 +466,21 @@ impl PartitionData { /// /// The latest known leader epoch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_leader_epoch(mut self, value: i32) -> Self { self.leader_epoch = value; self } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } } #[cfg(feature = "broker")] @@ -164,7 +490,18 @@ impl Encodable for PartitionData { types::Int16.encode(buf, &self.error_code)?; types::Int32.encode(buf, &self.leader_id)?; types::Int32.encode(buf, &self.leader_epoch)?; - + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + } Ok(()) } fn compute_size(&self, version: i16) -> Result { @@ -173,7 +510,18 @@ impl Encodable for PartitionData { total_size += types::Int16.compute_size(&self.error_code)?; total_size += types::Int32.compute_size(&self.leader_id)?; total_size += types::Int32.compute_size(&self.leader_epoch)?; - + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + } Ok(total_size) } } @@ -185,11 +533,22 @@ impl Decodable for PartitionData { let error_code = types::Int16.decode(buf)?; let leader_id = types::Int32.decode(buf)?; let leader_epoch = types::Int32.decode(buf)?; + let mut unknown_tagged_fields = BTreeMap::new(); + if version >= 1 { + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } Ok(Self { partition_index, error_code, leader_id, leader_epoch, + unknown_tagged_fields, }) } } @@ -201,28 +560,32 @@ impl Default for PartitionData { error_code: 0, leader_id: (0).into(), leader_epoch: 0, + unknown_tagged_fields: BTreeMap::new(), } } } impl Message for PartitionData { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct TopicData { /// The topic name. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub topic_name: super::TopicName, /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub partitions: Vec, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, } impl TopicData { @@ -230,7 +593,7 @@ impl TopicData { /// /// The topic name. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_topic_name(mut self, value: super::TopicName) -> Self { self.topic_name = value; self @@ -239,26 +602,75 @@ impl TopicData { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_partitions(mut self, value: Vec) -> Self { self.partitions = value; self } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } } #[cfg(feature = "broker")] impl Encodable for TopicData { fn encode(&self, buf: &mut B, version: i16) -> Result<()> { - types::String.encode(buf, &self.topic_name)?; - types::Array(types::Struct { version }).encode(buf, &self.partitions)?; - + if version >= 1 { + types::CompactString.encode(buf, &self.topic_name)?; + } else { + types::String.encode(buf, &self.topic_name)?; + } + if version >= 1 { + types::CompactArray(types::Struct { version }).encode(buf, &self.partitions)?; + } else { + types::Array(types::Struct { version }).encode(buf, &self.partitions)?; + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + } Ok(()) } fn compute_size(&self, version: i16) -> Result { let mut total_size = 0; - total_size += types::String.compute_size(&self.topic_name)?; - total_size += types::Array(types::Struct { version }).compute_size(&self.partitions)?; - + if version >= 1 { + total_size += types::CompactString.compute_size(&self.topic_name)?; + } else { + total_size += types::String.compute_size(&self.topic_name)?; + } + if version >= 1 { + total_size += + types::CompactArray(types::Struct { version }).compute_size(&self.partitions)?; + } else { + total_size += types::Array(types::Struct { version }).compute_size(&self.partitions)?; + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + } Ok(total_size) } } @@ -266,11 +678,30 @@ impl Encodable for TopicData { #[cfg(feature = "client")] impl Decodable for TopicData { fn decode(buf: &mut B, version: i16) -> Result { - let topic_name = types::String.decode(buf)?; - let partitions = types::Array(types::Struct { version }).decode(buf)?; + let topic_name = if version >= 1 { + types::CompactString.decode(buf)? + } else { + types::String.decode(buf)? + }; + let partitions = if version >= 1 { + types::CompactArray(types::Struct { version }).decode(buf)? + } else { + types::Array(types::Struct { version }).decode(buf)? + }; + let mut unknown_tagged_fields = BTreeMap::new(); + if version >= 1 { + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } Ok(Self { topic_name, partitions, + unknown_tagged_fields, }) } } @@ -280,17 +711,22 @@ impl Default for TopicData { Self { topic_name: Default::default(), partitions: Default::default(), + unknown_tagged_fields: BTreeMap::new(), } } } impl Message for TopicData { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } impl HeaderVersion for BeginQuorumEpochResponse { fn header_version(version: i16) -> i16 { - 0 + if version >= 1 { + 1 + } else { + 0 + } } } diff --git a/src/messages/broker_registration_request.rs b/src/messages/broker_registration_request.rs index ad5025b..3408ca8 100644 --- a/src/messages/broker_registration_request.rs +++ b/src/messages/broker_registration_request.rs @@ -17,53 +17,53 @@ use crate::protocol::{ Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, }; -/// Valid versions: 0-3 +/// Valid versions: 0-4 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct BrokerRegistrationRequest { /// The broker ID. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub broker_id: super::BrokerId, /// The cluster id of the broker process. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub cluster_id: StrBytes, /// The incarnation id of the broker process. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub incarnation_id: Uuid, /// The listeners of this broker /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub listeners: Vec, - /// The features on this broker + /// The features on this broker. Note: in v0-v3, features with MinSupportedVersion = 0 are omitted. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub features: Vec, /// The rack which this broker is in. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub rack: Option, /// If the required configurations for ZK migration are present, this value is set to true /// - /// Supported API versions: 1-3 + /// Supported API versions: 1-4 pub is_migrating_zk_broker: bool, /// Log directories configured in this broker which are available. /// - /// Supported API versions: 2-3 + /// Supported API versions: 2-4 pub log_dirs: Vec, /// The epoch before a clean shutdown. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub previous_broker_epoch: i64, /// Other tagged fields @@ -75,7 +75,7 @@ impl BrokerRegistrationRequest { /// /// The broker ID. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub fn with_broker_id(mut self, value: super::BrokerId) -> Self { self.broker_id = value; self @@ -84,7 +84,7 @@ impl BrokerRegistrationRequest { /// /// The cluster id of the broker process. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub fn with_cluster_id(mut self, value: StrBytes) -> Self { self.cluster_id = value; self @@ -93,7 +93,7 @@ impl BrokerRegistrationRequest { /// /// The incarnation id of the broker process. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub fn with_incarnation_id(mut self, value: Uuid) -> Self { self.incarnation_id = value; self @@ -102,16 +102,16 @@ impl BrokerRegistrationRequest { /// /// The listeners of this broker /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub fn with_listeners(mut self, value: Vec) -> Self { self.listeners = value; self } /// Sets `features` to the passed value. /// - /// The features on this broker + /// The features on this broker. Note: in v0-v3, features with MinSupportedVersion = 0 are omitted. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub fn with_features(mut self, value: Vec) -> Self { self.features = value; self @@ -120,7 +120,7 @@ impl BrokerRegistrationRequest { /// /// The rack which this broker is in. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub fn with_rack(mut self, value: Option) -> Self { self.rack = value; self @@ -129,7 +129,7 @@ impl BrokerRegistrationRequest { /// /// If the required configurations for ZK migration are present, this value is set to true /// - /// Supported API versions: 1-3 + /// Supported API versions: 1-4 pub fn with_is_migrating_zk_broker(mut self, value: bool) -> Self { self.is_migrating_zk_broker = value; self @@ -138,7 +138,7 @@ impl BrokerRegistrationRequest { /// /// Log directories configured in this broker which are available. /// - /// Supported API versions: 2-3 + /// Supported API versions: 2-4 pub fn with_log_dirs(mut self, value: Vec) -> Self { self.log_dirs = value; self @@ -147,7 +147,7 @@ impl BrokerRegistrationRequest { /// /// The epoch before a clean shutdown. /// - /// Supported API versions: 3 + /// Supported API versions: 3-4 pub fn with_previous_broker_epoch(mut self, value: i64) -> Self { self.previous_broker_epoch = value; self @@ -300,27 +300,27 @@ impl Default for BrokerRegistrationRequest { } impl Message for BrokerRegistrationRequest { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 3 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 4 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0-3 +/// Valid versions: 0-4 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct Feature { /// The feature name. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub name: StrBytes, /// The minimum supported feature level. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub min_supported_version: i16, /// The maximum supported feature level. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub max_supported_version: i16, /// Other tagged fields @@ -332,7 +332,7 @@ impl Feature { /// /// The feature name. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub fn with_name(mut self, value: StrBytes) -> Self { self.name = value; self @@ -341,7 +341,7 @@ impl Feature { /// /// The minimum supported feature level. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub fn with_min_supported_version(mut self, value: i16) -> Self { self.min_supported_version = value; self @@ -350,7 +350,7 @@ impl Feature { /// /// The maximum supported feature level. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub fn with_max_supported_version(mut self, value: i16) -> Self { self.max_supported_version = value; self @@ -439,32 +439,32 @@ impl Default for Feature { } impl Message for Feature { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 3 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 4 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0-3 +/// Valid versions: 0-4 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct Listener { /// The name of the endpoint. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub name: StrBytes, /// The hostname. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub host: StrBytes, /// The port. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub port: u16, /// The security protocol. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub security_protocol: i16, /// Other tagged fields @@ -476,7 +476,7 @@ impl Listener { /// /// The name of the endpoint. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub fn with_name(mut self, value: StrBytes) -> Self { self.name = value; self @@ -485,7 +485,7 @@ impl Listener { /// /// The hostname. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub fn with_host(mut self, value: StrBytes) -> Self { self.host = value; self @@ -494,7 +494,7 @@ impl Listener { /// /// The port. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub fn with_port(mut self, value: u16) -> Self { self.port = value; self @@ -503,7 +503,7 @@ impl Listener { /// /// The security protocol. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub fn with_security_protocol(mut self, value: i16) -> Self { self.security_protocol = value; self @@ -597,7 +597,7 @@ impl Default for Listener { } impl Message for Listener { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 3 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 4 }; const DEPRECATED_VERSIONS: Option = None; } diff --git a/src/messages/broker_registration_response.rs b/src/messages/broker_registration_response.rs index dbc2df4..c987fce 100644 --- a/src/messages/broker_registration_response.rs +++ b/src/messages/broker_registration_response.rs @@ -17,23 +17,23 @@ use crate::protocol::{ Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, }; -/// Valid versions: 0-3 +/// Valid versions: 0-4 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct BrokerRegistrationResponse { /// Duration in milliseconds for which the request was throttled due to a quota violation, or zero if the request did not violate any quota. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub throttle_time_ms: i32, /// The error code, or 0 if there was no error. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub error_code: i16, /// The broker's assigned epoch, or -1 if none was assigned. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub broker_epoch: i64, /// Other tagged fields @@ -45,7 +45,7 @@ impl BrokerRegistrationResponse { /// /// Duration in milliseconds for which the request was throttled due to a quota violation, or zero if the request did not violate any quota. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub fn with_throttle_time_ms(mut self, value: i32) -> Self { self.throttle_time_ms = value; self @@ -54,7 +54,7 @@ impl BrokerRegistrationResponse { /// /// The error code, or 0 if there was no error. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub fn with_error_code(mut self, value: i16) -> Self { self.error_code = value; self @@ -63,7 +63,7 @@ impl BrokerRegistrationResponse { /// /// The broker's assigned epoch, or -1 if none was assigned. /// - /// Supported API versions: 0-3 + /// Supported API versions: 0-4 pub fn with_broker_epoch(mut self, value: i64) -> Self { self.broker_epoch = value; self @@ -152,7 +152,7 @@ impl Default for BrokerRegistrationResponse { } impl Message for BrokerRegistrationResponse { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 3 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 4 }; const DEPRECATED_VERSIONS: Option = None; } diff --git a/src/messages/create_topics_request.rs b/src/messages/create_topics_request.rs index 18ee067..d309ad5 100644 --- a/src/messages/create_topics_request.rs +++ b/src/messages/create_topics_request.rs @@ -182,7 +182,7 @@ pub struct CreatableTopic { /// The custom topic configurations to set. /// /// Supported API versions: 0-7 - pub configs: Vec, + pub configs: Vec, /// Other tagged fields pub unknown_tagged_fields: BTreeMap, @@ -230,7 +230,7 @@ impl CreatableTopic { /// The custom topic configurations to set. /// /// Supported API versions: 0-7 - pub fn with_configs(mut self, value: Vec) -> Self { + pub fn with_configs(mut self, value: Vec) -> Self { self.configs = value; self } @@ -380,52 +380,38 @@ impl Message for CreatableTopic { /// Valid versions: 0-7 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] -pub struct CreateTopicsRequest { - /// The topics to create. +pub struct CreatableTopicConfig { + /// The configuration name. /// /// Supported API versions: 0-7 - pub topics: Vec, + pub name: StrBytes, - /// How long to wait in milliseconds before timing out the request. + /// The configuration value. /// /// Supported API versions: 0-7 - pub timeout_ms: i32, - - /// If true, check that the topics can be created as specified, but don't create anything. - /// - /// Supported API versions: 1-7 - pub validate_only: bool, + pub value: Option, /// Other tagged fields pub unknown_tagged_fields: BTreeMap, } -impl CreateTopicsRequest { - /// Sets `topics` to the passed value. +impl CreatableTopicConfig { + /// Sets `name` to the passed value. /// - /// The topics to create. + /// The configuration name. /// /// Supported API versions: 0-7 - pub fn with_topics(mut self, value: Vec) -> Self { - self.topics = value; + pub fn with_name(mut self, value: StrBytes) -> Self { + self.name = value; self } - /// Sets `timeout_ms` to the passed value. + /// Sets `value` to the passed value. /// - /// How long to wait in milliseconds before timing out the request. + /// The configuration value. /// /// Supported API versions: 0-7 - pub fn with_timeout_ms(mut self, value: i32) -> Self { - self.timeout_ms = value; - self - } - /// Sets `validate_only` to the passed value. - /// - /// If true, check that the topics can be created as specified, but don't create anything. - /// - /// Supported API versions: 1-7 - pub fn with_validate_only(mut self, value: bool) -> Self { - self.validate_only = value; + pub fn with_value(mut self, value: Option) -> Self { + self.value = value; self } /// Sets unknown_tagged_fields to the passed value. @@ -441,20 +427,17 @@ impl CreateTopicsRequest { } #[cfg(feature = "client")] -impl Encodable for CreateTopicsRequest { +impl Encodable for CreatableTopicConfig { fn encode(&self, buf: &mut B, version: i16) -> Result<()> { if version >= 5 { - types::CompactArray(types::Struct { version }).encode(buf, &self.topics)?; + types::CompactString.encode(buf, &self.name)?; } else { - types::Array(types::Struct { version }).encode(buf, &self.topics)?; + types::String.encode(buf, &self.name)?; } - types::Int32.encode(buf, &self.timeout_ms)?; - if version >= 1 { - types::Boolean.encode(buf, &self.validate_only)?; + if version >= 5 { + types::CompactString.encode(buf, &self.value)?; } else { - if self.validate_only { - bail!("A field is set that is not available on the selected protocol version"); - } + types::String.encode(buf, &self.value)?; } if version >= 5 { let num_tagged_fields = self.unknown_tagged_fields.len(); @@ -473,18 +456,14 @@ impl Encodable for CreateTopicsRequest { fn compute_size(&self, version: i16) -> Result { let mut total_size = 0; if version >= 5 { - total_size += - types::CompactArray(types::Struct { version }).compute_size(&self.topics)?; + total_size += types::CompactString.compute_size(&self.name)?; } else { - total_size += types::Array(types::Struct { version }).compute_size(&self.topics)?; + total_size += types::String.compute_size(&self.name)?; } - total_size += types::Int32.compute_size(&self.timeout_ms)?; - if version >= 1 { - total_size += types::Boolean.compute_size(&self.validate_only)?; + if version >= 5 { + total_size += types::CompactString.compute_size(&self.value)?; } else { - if self.validate_only { - bail!("A field is set that is not available on the selected protocol version"); - } + total_size += types::String.compute_size(&self.value)?; } if version >= 5 { let num_tagged_fields = self.unknown_tagged_fields.len(); @@ -503,18 +482,17 @@ impl Encodable for CreateTopicsRequest { } #[cfg(feature = "broker")] -impl Decodable for CreateTopicsRequest { +impl Decodable for CreatableTopicConfig { fn decode(buf: &mut B, version: i16) -> Result { - let topics = if version >= 5 { - types::CompactArray(types::Struct { version }).decode(buf)? + let name = if version >= 5 { + types::CompactString.decode(buf)? } else { - types::Array(types::Struct { version }).decode(buf)? + types::String.decode(buf)? }; - let timeout_ms = types::Int32.decode(buf)?; - let validate_only = if version >= 1 { - types::Boolean.decode(buf)? + let value = if version >= 5 { + types::CompactString.decode(buf)? } else { - false + types::String.decode(buf)? }; let mut unknown_tagged_fields = BTreeMap::new(); if version >= 5 { @@ -527,26 +505,24 @@ impl Decodable for CreateTopicsRequest { } } Ok(Self { - topics, - timeout_ms, - validate_only, + name, + value, unknown_tagged_fields, }) } } -impl Default for CreateTopicsRequest { +impl Default for CreatableTopicConfig { fn default() -> Self { Self { - topics: Default::default(), - timeout_ms: 60000, - validate_only: false, + name: Default::default(), + value: Some(Default::default()), unknown_tagged_fields: BTreeMap::new(), } } } -impl Message for CreateTopicsRequest { +impl Message for CreatableTopicConfig { const VERSIONS: VersionRange = VersionRange { min: 0, max: 7 }; const DEPRECATED_VERSIONS: Option = Some(VersionRange { min: 0, max: 1 }); } @@ -554,38 +530,52 @@ impl Message for CreateTopicsRequest { /// Valid versions: 0-7 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] -pub struct CreateableTopicConfig { - /// The configuration name. +pub struct CreateTopicsRequest { + /// The topics to create. /// /// Supported API versions: 0-7 - pub name: StrBytes, + pub topics: Vec, - /// The configuration value. + /// How long to wait in milliseconds before timing out the request. /// /// Supported API versions: 0-7 - pub value: Option, + pub timeout_ms: i32, + + /// If true, check that the topics can be created as specified, but don't create anything. + /// + /// Supported API versions: 1-7 + pub validate_only: bool, /// Other tagged fields pub unknown_tagged_fields: BTreeMap, } -impl CreateableTopicConfig { - /// Sets `name` to the passed value. +impl CreateTopicsRequest { + /// Sets `topics` to the passed value. /// - /// The configuration name. + /// The topics to create. /// /// Supported API versions: 0-7 - pub fn with_name(mut self, value: StrBytes) -> Self { - self.name = value; + pub fn with_topics(mut self, value: Vec) -> Self { + self.topics = value; self } - /// Sets `value` to the passed value. + /// Sets `timeout_ms` to the passed value. /// - /// The configuration value. + /// How long to wait in milliseconds before timing out the request. /// /// Supported API versions: 0-7 - pub fn with_value(mut self, value: Option) -> Self { - self.value = value; + pub fn with_timeout_ms(mut self, value: i32) -> Self { + self.timeout_ms = value; + self + } + /// Sets `validate_only` to the passed value. + /// + /// If true, check that the topics can be created as specified, but don't create anything. + /// + /// Supported API versions: 1-7 + pub fn with_validate_only(mut self, value: bool) -> Self { + self.validate_only = value; self } /// Sets unknown_tagged_fields to the passed value. @@ -601,17 +591,20 @@ impl CreateableTopicConfig { } #[cfg(feature = "client")] -impl Encodable for CreateableTopicConfig { +impl Encodable for CreateTopicsRequest { fn encode(&self, buf: &mut B, version: i16) -> Result<()> { if version >= 5 { - types::CompactString.encode(buf, &self.name)?; + types::CompactArray(types::Struct { version }).encode(buf, &self.topics)?; } else { - types::String.encode(buf, &self.name)?; + types::Array(types::Struct { version }).encode(buf, &self.topics)?; } - if version >= 5 { - types::CompactString.encode(buf, &self.value)?; + types::Int32.encode(buf, &self.timeout_ms)?; + if version >= 1 { + types::Boolean.encode(buf, &self.validate_only)?; } else { - types::String.encode(buf, &self.value)?; + if self.validate_only { + bail!("A field is set that is not available on the selected protocol version"); + } } if version >= 5 { let num_tagged_fields = self.unknown_tagged_fields.len(); @@ -630,14 +623,18 @@ impl Encodable for CreateableTopicConfig { fn compute_size(&self, version: i16) -> Result { let mut total_size = 0; if version >= 5 { - total_size += types::CompactString.compute_size(&self.name)?; + total_size += + types::CompactArray(types::Struct { version }).compute_size(&self.topics)?; } else { - total_size += types::String.compute_size(&self.name)?; + total_size += types::Array(types::Struct { version }).compute_size(&self.topics)?; } - if version >= 5 { - total_size += types::CompactString.compute_size(&self.value)?; + total_size += types::Int32.compute_size(&self.timeout_ms)?; + if version >= 1 { + total_size += types::Boolean.compute_size(&self.validate_only)?; } else { - total_size += types::String.compute_size(&self.value)?; + if self.validate_only { + bail!("A field is set that is not available on the selected protocol version"); + } } if version >= 5 { let num_tagged_fields = self.unknown_tagged_fields.len(); @@ -656,17 +653,18 @@ impl Encodable for CreateableTopicConfig { } #[cfg(feature = "broker")] -impl Decodable for CreateableTopicConfig { +impl Decodable for CreateTopicsRequest { fn decode(buf: &mut B, version: i16) -> Result { - let name = if version >= 5 { - types::CompactString.decode(buf)? + let topics = if version >= 5 { + types::CompactArray(types::Struct { version }).decode(buf)? } else { - types::String.decode(buf)? + types::Array(types::Struct { version }).decode(buf)? }; - let value = if version >= 5 { - types::CompactString.decode(buf)? + let timeout_ms = types::Int32.decode(buf)?; + let validate_only = if version >= 1 { + types::Boolean.decode(buf)? } else { - types::String.decode(buf)? + false }; let mut unknown_tagged_fields = BTreeMap::new(); if version >= 5 { @@ -679,24 +677,26 @@ impl Decodable for CreateableTopicConfig { } } Ok(Self { - name, - value, + topics, + timeout_ms, + validate_only, unknown_tagged_fields, }) } } -impl Default for CreateableTopicConfig { +impl Default for CreateTopicsRequest { fn default() -> Self { Self { - name: Default::default(), - value: Some(Default::default()), + topics: Default::default(), + timeout_ms: 60000, + validate_only: false, unknown_tagged_fields: BTreeMap::new(), } } } -impl Message for CreateableTopicConfig { +impl Message for CreateTopicsRequest { const VERSIONS: VersionRange = VersionRange { min: 0, max: 7 }; const DEPRECATED_VERSIONS: Option = Some(VersionRange { min: 0, max: 1 }); } diff --git a/src/messages/describe_quorum_request.rs b/src/messages/describe_quorum_request.rs index 3efa8e6..513cc77 100644 --- a/src/messages/describe_quorum_request.rs +++ b/src/messages/describe_quorum_request.rs @@ -17,13 +17,13 @@ use crate::protocol::{ Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, }; -/// Valid versions: 0-1 +/// Valid versions: 0-2 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct DescribeQuorumRequest { /// /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub topics: Vec, /// Other tagged fields @@ -35,7 +35,7 @@ impl DescribeQuorumRequest { /// /// /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub fn with_topics(mut self, value: Vec) -> Self { self.topics = value; self @@ -114,17 +114,17 @@ impl Default for DescribeQuorumRequest { } impl Message for DescribeQuorumRequest { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 2 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0-1 +/// Valid versions: 0-2 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct PartitionData { /// The partition index. /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub partition_index: i32, /// Other tagged fields @@ -136,7 +136,7 @@ impl PartitionData { /// /// The partition index. /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub fn with_partition_index(mut self, value: i32) -> Self { self.partition_index = value; self @@ -215,22 +215,22 @@ impl Default for PartitionData { } impl Message for PartitionData { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 2 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0-1 +/// Valid versions: 0-2 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct TopicData { /// The topic name. /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub topic_name: super::TopicName, /// /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub partitions: Vec, /// Other tagged fields @@ -242,7 +242,7 @@ impl TopicData { /// /// The topic name. /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub fn with_topic_name(mut self, value: super::TopicName) -> Self { self.topic_name = value; self @@ -251,7 +251,7 @@ impl TopicData { /// /// /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub fn with_partitions(mut self, value: Vec) -> Self { self.partitions = value; self @@ -336,7 +336,7 @@ impl Default for TopicData { } impl Message for TopicData { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 2 }; const DEPRECATED_VERSIONS: Option = None; } diff --git a/src/messages/describe_quorum_response.rs b/src/messages/describe_quorum_response.rs index a2f4974..fb874b2 100644 --- a/src/messages/describe_quorum_response.rs +++ b/src/messages/describe_quorum_response.rs @@ -17,20 +17,30 @@ use crate::protocol::{ Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, }; -/// Valid versions: 0-1 +/// Valid versions: 0-2 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct DescribeQuorumResponse { /// The top level error code. /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub error_code: i16, + /// The error message, or null if there was no error. + /// + /// Supported API versions: 2 + pub error_message: Option, + /// /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub topics: Vec, + /// + /// + /// Supported API versions: 2 + pub nodes: Vec, + /// Other tagged fields pub unknown_tagged_fields: BTreeMap, } @@ -40,20 +50,38 @@ impl DescribeQuorumResponse { /// /// The top level error code. /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub fn with_error_code(mut self, value: i16) -> Self { self.error_code = value; self } + /// Sets `error_message` to the passed value. + /// + /// The error message, or null if there was no error. + /// + /// Supported API versions: 2 + pub fn with_error_message(mut self, value: Option) -> Self { + self.error_message = value; + self + } /// Sets `topics` to the passed value. /// /// /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub fn with_topics(mut self, value: Vec) -> Self { self.topics = value; self } + /// Sets `nodes` to the passed value. + /// + /// + /// + /// Supported API versions: 2 + pub fn with_nodes(mut self, value: Vec) -> Self { + self.nodes = value; + self + } /// Sets unknown_tagged_fields to the passed value. pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { self.unknown_tagged_fields = value; @@ -70,7 +98,17 @@ impl DescribeQuorumResponse { impl Encodable for DescribeQuorumResponse { fn encode(&self, buf: &mut B, version: i16) -> Result<()> { types::Int16.encode(buf, &self.error_code)?; + if version >= 2 { + types::CompactString.encode(buf, &self.error_message)?; + } types::CompactArray(types::Struct { version }).encode(buf, &self.topics)?; + if version >= 2 { + types::CompactArray(types::Struct { version }).encode(buf, &self.nodes)?; + } else { + if !self.nodes.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } let num_tagged_fields = self.unknown_tagged_fields.len(); if num_tagged_fields > std::u32::MAX as usize { bail!( @@ -86,7 +124,18 @@ impl Encodable for DescribeQuorumResponse { fn compute_size(&self, version: i16) -> Result { let mut total_size = 0; total_size += types::Int16.compute_size(&self.error_code)?; + if version >= 2 { + total_size += types::CompactString.compute_size(&self.error_message)?; + } total_size += types::CompactArray(types::Struct { version }).compute_size(&self.topics)?; + if version >= 2 { + total_size += + types::CompactArray(types::Struct { version }).compute_size(&self.nodes)?; + } else { + if !self.nodes.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } let num_tagged_fields = self.unknown_tagged_fields.len(); if num_tagged_fields > std::u32::MAX as usize { bail!( @@ -105,7 +154,17 @@ impl Encodable for DescribeQuorumResponse { impl Decodable for DescribeQuorumResponse { fn decode(buf: &mut B, version: i16) -> Result { let error_code = types::Int16.decode(buf)?; + let error_message = if version >= 2 { + types::CompactString.decode(buf)? + } else { + Some(Default::default()) + }; let topics = types::CompactArray(types::Struct { version }).decode(buf)?; + let nodes = if version >= 2 { + types::CompactArray(types::Struct { version }).decode(buf)? + } else { + Default::default() + }; let mut unknown_tagged_fields = BTreeMap::new(); let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; for _ in 0..num_tagged_fields { @@ -116,7 +175,9 @@ impl Decodable for DescribeQuorumResponse { } Ok(Self { error_code, + error_message, topics, + nodes, unknown_tagged_fields, }) } @@ -126,54 +187,401 @@ impl Default for DescribeQuorumResponse { fn default() -> Self { Self { error_code: 0, + error_message: Some(Default::default()), topics: Default::default(), + nodes: Default::default(), unknown_tagged_fields: BTreeMap::new(), } } } impl Message for DescribeQuorumResponse { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 2 }; + const DEPRECATED_VERSIONS: Option = None; +} + +/// Valid versions: 0-2 +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct Listener { + /// The name of the endpoint + /// + /// Supported API versions: 2 + pub name: StrBytes, + + /// The hostname + /// + /// Supported API versions: 2 + pub host: StrBytes, + + /// The port + /// + /// Supported API versions: 2 + pub port: u16, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, +} + +impl Listener { + /// Sets `name` to the passed value. + /// + /// The name of the endpoint + /// + /// Supported API versions: 2 + pub fn with_name(mut self, value: StrBytes) -> Self { + self.name = value; + self + } + /// Sets `host` to the passed value. + /// + /// The hostname + /// + /// Supported API versions: 2 + pub fn with_host(mut self, value: StrBytes) -> Self { + self.host = value; + self + } + /// Sets `port` to the passed value. + /// + /// The port + /// + /// Supported API versions: 2 + pub fn with_port(mut self, value: u16) -> Self { + self.port = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } +} + +#[cfg(feature = "broker")] +impl Encodable for Listener { + fn encode(&self, buf: &mut B, version: i16) -> Result<()> { + if version >= 2 { + types::CompactString.encode(buf, &self.name)?; + } else { + if !self.name.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 2 { + types::CompactString.encode(buf, &self.host)?; + } else { + if !self.host.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 2 { + types::UInt16.encode(buf, &self.port)?; + } else { + if self.port != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + Ok(()) + } + fn compute_size(&self, version: i16) -> Result { + let mut total_size = 0; + if version >= 2 { + total_size += types::CompactString.compute_size(&self.name)?; + } else { + if !self.name.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 2 { + total_size += types::CompactString.compute_size(&self.host)?; + } else { + if !self.host.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 2 { + total_size += types::UInt16.compute_size(&self.port)?; + } else { + if self.port != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + Ok(total_size) + } +} + +#[cfg(feature = "client")] +impl Decodable for Listener { + fn decode(buf: &mut B, version: i16) -> Result { + let name = if version >= 2 { + types::CompactString.decode(buf)? + } else { + Default::default() + }; + let host = if version >= 2 { + types::CompactString.decode(buf)? + } else { + Default::default() + }; + let port = if version >= 2 { + types::UInt16.decode(buf)? + } else { + 0 + }; + let mut unknown_tagged_fields = BTreeMap::new(); + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + Ok(Self { + name, + host, + port, + unknown_tagged_fields, + }) + } +} + +impl Default for Listener { + fn default() -> Self { + Self { + name: Default::default(), + host: Default::default(), + port: 0, + unknown_tagged_fields: BTreeMap::new(), + } + } +} + +impl Message for Listener { + const VERSIONS: VersionRange = VersionRange { min: 0, max: 2 }; + const DEPRECATED_VERSIONS: Option = None; +} + +/// Valid versions: 0-2 +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct Node { + /// The ID of the associated node + /// + /// Supported API versions: 2 + pub node_id: super::BrokerId, + + /// The listeners of this controller + /// + /// Supported API versions: 2 + pub listeners: Vec, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, +} + +impl Node { + /// Sets `node_id` to the passed value. + /// + /// The ID of the associated node + /// + /// Supported API versions: 2 + pub fn with_node_id(mut self, value: super::BrokerId) -> Self { + self.node_id = value; + self + } + /// Sets `listeners` to the passed value. + /// + /// The listeners of this controller + /// + /// Supported API versions: 2 + pub fn with_listeners(mut self, value: Vec) -> Self { + self.listeners = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } +} + +#[cfg(feature = "broker")] +impl Encodable for Node { + fn encode(&self, buf: &mut B, version: i16) -> Result<()> { + if version >= 2 { + types::Int32.encode(buf, &self.node_id)?; + } else { + if self.node_id != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 2 { + types::CompactArray(types::Struct { version }).encode(buf, &self.listeners)?; + } else { + if !self.listeners.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + Ok(()) + } + fn compute_size(&self, version: i16) -> Result { + let mut total_size = 0; + if version >= 2 { + total_size += types::Int32.compute_size(&self.node_id)?; + } else { + if self.node_id != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 2 { + total_size += + types::CompactArray(types::Struct { version }).compute_size(&self.listeners)?; + } else { + if !self.listeners.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + Ok(total_size) + } +} + +#[cfg(feature = "client")] +impl Decodable for Node { + fn decode(buf: &mut B, version: i16) -> Result { + let node_id = if version >= 2 { + types::Int32.decode(buf)? + } else { + (0).into() + }; + let listeners = if version >= 2 { + types::CompactArray(types::Struct { version }).decode(buf)? + } else { + Default::default() + }; + let mut unknown_tagged_fields = BTreeMap::new(); + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + Ok(Self { + node_id, + listeners, + unknown_tagged_fields, + }) + } +} + +impl Default for Node { + fn default() -> Self { + Self { + node_id: (0).into(), + listeners: Default::default(), + unknown_tagged_fields: BTreeMap::new(), + } + } +} + +impl Message for Node { + const VERSIONS: VersionRange = VersionRange { min: 0, max: 2 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0-1 +/// Valid versions: 0-2 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct PartitionData { /// The partition index. /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub partition_index: i32, /// /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub error_code: i16, + /// The error message, or null if there was no error. + /// + /// Supported API versions: 2 + pub error_message: Option, + /// The ID of the current leader or -1 if the leader is unknown. /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub leader_id: super::BrokerId, /// The latest known leader epoch /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub leader_epoch: i32, /// /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub high_watermark: i64, /// /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub current_voters: Vec, /// /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub observers: Vec, /// Other tagged fields @@ -185,7 +593,7 @@ impl PartitionData { /// /// The partition index. /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub fn with_partition_index(mut self, value: i32) -> Self { self.partition_index = value; self @@ -194,16 +602,25 @@ impl PartitionData { /// /// /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub fn with_error_code(mut self, value: i16) -> Self { self.error_code = value; self } + /// Sets `error_message` to the passed value. + /// + /// The error message, or null if there was no error. + /// + /// Supported API versions: 2 + pub fn with_error_message(mut self, value: Option) -> Self { + self.error_message = value; + self + } /// Sets `leader_id` to the passed value. /// /// The ID of the current leader or -1 if the leader is unknown. /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub fn with_leader_id(mut self, value: super::BrokerId) -> Self { self.leader_id = value; self @@ -212,7 +629,7 @@ impl PartitionData { /// /// The latest known leader epoch /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub fn with_leader_epoch(mut self, value: i32) -> Self { self.leader_epoch = value; self @@ -221,7 +638,7 @@ impl PartitionData { /// /// /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub fn with_high_watermark(mut self, value: i64) -> Self { self.high_watermark = value; self @@ -230,7 +647,7 @@ impl PartitionData { /// /// /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub fn with_current_voters(mut self, value: Vec) -> Self { self.current_voters = value; self @@ -239,7 +656,7 @@ impl PartitionData { /// /// /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub fn with_observers(mut self, value: Vec) -> Self { self.observers = value; self @@ -261,6 +678,9 @@ impl Encodable for PartitionData { fn encode(&self, buf: &mut B, version: i16) -> Result<()> { types::Int32.encode(buf, &self.partition_index)?; types::Int16.encode(buf, &self.error_code)?; + if version >= 2 { + types::CompactString.encode(buf, &self.error_message)?; + } types::Int32.encode(buf, &self.leader_id)?; types::Int32.encode(buf, &self.leader_epoch)?; types::Int64.encode(buf, &self.high_watermark)?; @@ -282,6 +702,9 @@ impl Encodable for PartitionData { let mut total_size = 0; total_size += types::Int32.compute_size(&self.partition_index)?; total_size += types::Int16.compute_size(&self.error_code)?; + if version >= 2 { + total_size += types::CompactString.compute_size(&self.error_message)?; + } total_size += types::Int32.compute_size(&self.leader_id)?; total_size += types::Int32.compute_size(&self.leader_epoch)?; total_size += types::Int64.compute_size(&self.high_watermark)?; @@ -308,6 +731,11 @@ impl Decodable for PartitionData { fn decode(buf: &mut B, version: i16) -> Result { let partition_index = types::Int32.decode(buf)?; let error_code = types::Int16.decode(buf)?; + let error_message = if version >= 2 { + types::CompactString.decode(buf)? + } else { + Some(Default::default()) + }; let leader_id = types::Int32.decode(buf)?; let leader_epoch = types::Int32.decode(buf)?; let high_watermark = types::Int64.decode(buf)?; @@ -324,6 +752,7 @@ impl Decodable for PartitionData { Ok(Self { partition_index, error_code, + error_message, leader_id, leader_epoch, high_watermark, @@ -339,6 +768,7 @@ impl Default for PartitionData { Self { partition_index: 0, error_code: 0, + error_message: Some(Default::default()), leader_id: (0).into(), leader_epoch: 0, high_watermark: 0, @@ -350,32 +780,37 @@ impl Default for PartitionData { } impl Message for PartitionData { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 2 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0-1 +/// Valid versions: 0-2 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct ReplicaState { /// /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub replica_id: super::BrokerId, + /// + /// + /// Supported API versions: 2 + pub replica_directory_id: Uuid, + /// The last known log end offset of the follower or -1 if it is unknown /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub log_end_offset: i64, /// The last known leader wall clock time time when a follower fetched from the leader. This is reported as -1 both for the current leader or if it is unknown for a voter /// - /// Supported API versions: 1 + /// Supported API versions: 1-2 pub last_fetch_timestamp: i64, /// The leader wall clock append time of the offset for which the follower made the most recent fetch request. This is reported as the current time for the leader and -1 if unknown for a voter /// - /// Supported API versions: 1 + /// Supported API versions: 1-2 pub last_caught_up_timestamp: i64, /// Other tagged fields @@ -387,16 +822,25 @@ impl ReplicaState { /// /// /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub fn with_replica_id(mut self, value: super::BrokerId) -> Self { self.replica_id = value; self } + /// Sets `replica_directory_id` to the passed value. + /// + /// + /// + /// Supported API versions: 2 + pub fn with_replica_directory_id(mut self, value: Uuid) -> Self { + self.replica_directory_id = value; + self + } /// Sets `log_end_offset` to the passed value. /// /// The last known log end offset of the follower or -1 if it is unknown /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub fn with_log_end_offset(mut self, value: i64) -> Self { self.log_end_offset = value; self @@ -405,7 +849,7 @@ impl ReplicaState { /// /// The last known leader wall clock time time when a follower fetched from the leader. This is reported as -1 both for the current leader or if it is unknown for a voter /// - /// Supported API versions: 1 + /// Supported API versions: 1-2 pub fn with_last_fetch_timestamp(mut self, value: i64) -> Self { self.last_fetch_timestamp = value; self @@ -414,7 +858,7 @@ impl ReplicaState { /// /// The leader wall clock append time of the offset for which the follower made the most recent fetch request. This is reported as the current time for the leader and -1 if unknown for a voter /// - /// Supported API versions: 1 + /// Supported API versions: 1-2 pub fn with_last_caught_up_timestamp(mut self, value: i64) -> Self { self.last_caught_up_timestamp = value; self @@ -435,6 +879,13 @@ impl ReplicaState { impl Encodable for ReplicaState { fn encode(&self, buf: &mut B, version: i16) -> Result<()> { types::Int32.encode(buf, &self.replica_id)?; + if version >= 2 { + types::Uuid.encode(buf, &self.replica_directory_id)?; + } else { + if &self.replica_directory_id != &Uuid::nil() { + bail!("A field is set that is not available on the selected protocol version"); + } + } types::Int64.encode(buf, &self.log_end_offset)?; if version >= 1 { types::Int64.encode(buf, &self.last_fetch_timestamp)?; @@ -457,6 +908,13 @@ impl Encodable for ReplicaState { fn compute_size(&self, version: i16) -> Result { let mut total_size = 0; total_size += types::Int32.compute_size(&self.replica_id)?; + if version >= 2 { + total_size += types::Uuid.compute_size(&self.replica_directory_id)?; + } else { + if &self.replica_directory_id != &Uuid::nil() { + bail!("A field is set that is not available on the selected protocol version"); + } + } total_size += types::Int64.compute_size(&self.log_end_offset)?; if version >= 1 { total_size += types::Int64.compute_size(&self.last_fetch_timestamp)?; @@ -482,6 +940,11 @@ impl Encodable for ReplicaState { impl Decodable for ReplicaState { fn decode(buf: &mut B, version: i16) -> Result { let replica_id = types::Int32.decode(buf)?; + let replica_directory_id = if version >= 2 { + types::Uuid.decode(buf)? + } else { + Uuid::nil() + }; let log_end_offset = types::Int64.decode(buf)?; let last_fetch_timestamp = if version >= 1 { types::Int64.decode(buf)? @@ -503,6 +966,7 @@ impl Decodable for ReplicaState { } Ok(Self { replica_id, + replica_directory_id, log_end_offset, last_fetch_timestamp, last_caught_up_timestamp, @@ -515,6 +979,7 @@ impl Default for ReplicaState { fn default() -> Self { Self { replica_id: (0).into(), + replica_directory_id: Uuid::nil(), log_end_offset: 0, last_fetch_timestamp: -1, last_caught_up_timestamp: -1, @@ -524,22 +989,22 @@ impl Default for ReplicaState { } impl Message for ReplicaState { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 2 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0-1 +/// Valid versions: 0-2 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct TopicData { /// The topic name. /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub topic_name: super::TopicName, /// /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub partitions: Vec, /// Other tagged fields @@ -551,7 +1016,7 @@ impl TopicData { /// /// The topic name. /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub fn with_topic_name(mut self, value: super::TopicName) -> Self { self.topic_name = value; self @@ -560,7 +1025,7 @@ impl TopicData { /// /// /// - /// Supported API versions: 0-1 + /// Supported API versions: 0-2 pub fn with_partitions(mut self, value: Vec) -> Self { self.partitions = value; self @@ -645,7 +1110,7 @@ impl Default for TopicData { } impl Message for TopicData { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 2 }; const DEPRECATED_VERSIONS: Option = None; } diff --git a/src/messages/end_quorum_epoch_request.rs b/src/messages/end_quorum_epoch_request.rs index 692d226..072cf49 100644 --- a/src/messages/end_quorum_epoch_request.rs +++ b/src/messages/end_quorum_epoch_request.rs @@ -17,19 +17,27 @@ use crate::protocol::{ Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, }; -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct EndQuorumEpochRequest { /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub cluster_id: Option, /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub topics: Vec, + + /// Endpoints for the leader + /// + /// Supported API versions: 1 + pub leader_endpoints: Vec, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, } impl EndQuorumEpochRequest { @@ -37,7 +45,7 @@ impl EndQuorumEpochRequest { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_cluster_id(mut self, value: Option) -> Self { self.cluster_id = value; self @@ -46,26 +54,91 @@ impl EndQuorumEpochRequest { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_topics(mut self, value: Vec) -> Self { self.topics = value; self } + /// Sets `leader_endpoints` to the passed value. + /// + /// Endpoints for the leader + /// + /// Supported API versions: 1 + pub fn with_leader_endpoints(mut self, value: Vec) -> Self { + self.leader_endpoints = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } } #[cfg(feature = "client")] impl Encodable for EndQuorumEpochRequest { fn encode(&self, buf: &mut B, version: i16) -> Result<()> { - types::String.encode(buf, &self.cluster_id)?; - types::Array(types::Struct { version }).encode(buf, &self.topics)?; - + if version >= 1 { + types::CompactString.encode(buf, &self.cluster_id)?; + } else { + types::String.encode(buf, &self.cluster_id)?; + } + if version >= 1 { + types::CompactArray(types::Struct { version }).encode(buf, &self.topics)?; + } else { + types::Array(types::Struct { version }).encode(buf, &self.topics)?; + } + if version >= 1 { + types::CompactArray(types::Struct { version }).encode(buf, &self.leader_endpoints)?; + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + } Ok(()) } fn compute_size(&self, version: i16) -> Result { let mut total_size = 0; - total_size += types::String.compute_size(&self.cluster_id)?; - total_size += types::Array(types::Struct { version }).compute_size(&self.topics)?; - + if version >= 1 { + total_size += types::CompactString.compute_size(&self.cluster_id)?; + } else { + total_size += types::String.compute_size(&self.cluster_id)?; + } + if version >= 1 { + total_size += + types::CompactArray(types::Struct { version }).compute_size(&self.topics)?; + } else { + total_size += types::Array(types::Struct { version }).compute_size(&self.topics)?; + } + if version >= 1 { + total_size += types::CompactArray(types::Struct { version }) + .compute_size(&self.leader_endpoints)?; + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + } Ok(total_size) } } @@ -73,9 +146,37 @@ impl Encodable for EndQuorumEpochRequest { #[cfg(feature = "broker")] impl Decodable for EndQuorumEpochRequest { fn decode(buf: &mut B, version: i16) -> Result { - let cluster_id = types::String.decode(buf)?; - let topics = types::Array(types::Struct { version }).decode(buf)?; - Ok(Self { cluster_id, topics }) + let cluster_id = if version >= 1 { + types::CompactString.decode(buf)? + } else { + types::String.decode(buf)? + }; + let topics = if version >= 1 { + types::CompactArray(types::Struct { version }).decode(buf)? + } else { + types::Array(types::Struct { version }).decode(buf)? + }; + let leader_endpoints = if version >= 1 { + types::CompactArray(types::Struct { version }).decode(buf)? + } else { + Default::default() + }; + let mut unknown_tagged_fields = BTreeMap::new(); + if version >= 1 { + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } + Ok(Self { + cluster_id, + topics, + leader_endpoints, + unknown_tagged_fields, + }) } } @@ -84,38 +185,241 @@ impl Default for EndQuorumEpochRequest { Self { cluster_id: None, topics: Default::default(), + leader_endpoints: Default::default(), + unknown_tagged_fields: BTreeMap::new(), } } } impl Message for EndQuorumEpochRequest { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; + const DEPRECATED_VERSIONS: Option = None; +} + +/// Valid versions: 0-1 +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct LeaderEndpoint { + /// The name of the endpoint + /// + /// Supported API versions: 1 + pub name: StrBytes, + + /// The node's hostname + /// + /// Supported API versions: 1 + pub host: StrBytes, + + /// The node's port + /// + /// Supported API versions: 1 + pub port: u16, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, +} + +impl LeaderEndpoint { + /// Sets `name` to the passed value. + /// + /// The name of the endpoint + /// + /// Supported API versions: 1 + pub fn with_name(mut self, value: StrBytes) -> Self { + self.name = value; + self + } + /// Sets `host` to the passed value. + /// + /// The node's hostname + /// + /// Supported API versions: 1 + pub fn with_host(mut self, value: StrBytes) -> Self { + self.host = value; + self + } + /// Sets `port` to the passed value. + /// + /// The node's port + /// + /// Supported API versions: 1 + pub fn with_port(mut self, value: u16) -> Self { + self.port = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } +} + +#[cfg(feature = "client")] +impl Encodable for LeaderEndpoint { + fn encode(&self, buf: &mut B, version: i16) -> Result<()> { + if version >= 1 { + types::CompactString.encode(buf, &self.name)?; + } else { + if !self.name.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + types::CompactString.encode(buf, &self.host)?; + } else { + if !self.host.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + types::UInt16.encode(buf, &self.port)?; + } else { + if self.port != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + } + Ok(()) + } + fn compute_size(&self, version: i16) -> Result { + let mut total_size = 0; + if version >= 1 { + total_size += types::CompactString.compute_size(&self.name)?; + } else { + if !self.name.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + total_size += types::CompactString.compute_size(&self.host)?; + } else { + if !self.host.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + total_size += types::UInt16.compute_size(&self.port)?; + } else { + if self.port != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + } + Ok(total_size) + } +} + +#[cfg(feature = "broker")] +impl Decodable for LeaderEndpoint { + fn decode(buf: &mut B, version: i16) -> Result { + let name = if version >= 1 { + types::CompactString.decode(buf)? + } else { + Default::default() + }; + let host = if version >= 1 { + types::CompactString.decode(buf)? + } else { + Default::default() + }; + let port = if version >= 1 { + types::UInt16.decode(buf)? + } else { + 0 + }; + let mut unknown_tagged_fields = BTreeMap::new(); + if version >= 1 { + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } + Ok(Self { + name, + host, + port, + unknown_tagged_fields, + }) + } +} + +impl Default for LeaderEndpoint { + fn default() -> Self { + Self { + name: Default::default(), + host: Default::default(), + port: 0, + unknown_tagged_fields: BTreeMap::new(), + } + } +} + +impl Message for LeaderEndpoint { + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct PartitionData { /// The partition index. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub partition_index: i32, /// The current leader ID that is resigning /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub leader_id: super::BrokerId, /// The current epoch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub leader_epoch: i32, /// A sorted list of preferred successors to start the election /// /// Supported API versions: 0 pub preferred_successors: Vec, + + /// A sorted list of preferred candidates to start the election + /// + /// Supported API versions: 1 + pub preferred_candidates: Vec, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, } impl PartitionData { @@ -123,7 +427,7 @@ impl PartitionData { /// /// The partition index. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_partition_index(mut self, value: i32) -> Self { self.partition_index = value; self @@ -132,7 +436,7 @@ impl PartitionData { /// /// The current leader ID that is resigning /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_leader_id(mut self, value: super::BrokerId) -> Self { self.leader_id = value; self @@ -141,7 +445,7 @@ impl PartitionData { /// /// The current epoch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_leader_epoch(mut self, value: i32) -> Self { self.leader_epoch = value; self @@ -155,6 +459,25 @@ impl PartitionData { self.preferred_successors = value; self } + /// Sets `preferred_candidates` to the passed value. + /// + /// A sorted list of preferred candidates to start the election + /// + /// Supported API versions: 1 + pub fn with_preferred_candidates(mut self, value: Vec) -> Self { + self.preferred_candidates = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } } #[cfg(feature = "client")] @@ -163,8 +486,25 @@ impl Encodable for PartitionData { types::Int32.encode(buf, &self.partition_index)?; types::Int32.encode(buf, &self.leader_id)?; types::Int32.encode(buf, &self.leader_epoch)?; - types::Array(types::Int32).encode(buf, &self.preferred_successors)?; - + if version == 0 { + types::Array(types::Int32).encode(buf, &self.preferred_successors)?; + } + if version >= 1 { + types::CompactArray(types::Struct { version }) + .encode(buf, &self.preferred_candidates)?; + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + } Ok(()) } fn compute_size(&self, version: i16) -> Result { @@ -172,8 +512,25 @@ impl Encodable for PartitionData { total_size += types::Int32.compute_size(&self.partition_index)?; total_size += types::Int32.compute_size(&self.leader_id)?; total_size += types::Int32.compute_size(&self.leader_epoch)?; - total_size += types::Array(types::Int32).compute_size(&self.preferred_successors)?; - + if version == 0 { + total_size += types::Array(types::Int32).compute_size(&self.preferred_successors)?; + } + if version >= 1 { + total_size += types::CompactArray(types::Struct { version }) + .compute_size(&self.preferred_candidates)?; + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + } Ok(total_size) } } @@ -184,12 +541,33 @@ impl Decodable for PartitionData { let partition_index = types::Int32.decode(buf)?; let leader_id = types::Int32.decode(buf)?; let leader_epoch = types::Int32.decode(buf)?; - let preferred_successors = types::Array(types::Int32).decode(buf)?; + let preferred_successors = if version == 0 { + types::Array(types::Int32).decode(buf)? + } else { + Default::default() + }; + let preferred_candidates = if version >= 1 { + types::CompactArray(types::Struct { version }).decode(buf)? + } else { + Default::default() + }; + let mut unknown_tagged_fields = BTreeMap::new(); + if version >= 1 { + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } Ok(Self { partition_index, leader_id, leader_epoch, preferred_successors, + preferred_candidates, + unknown_tagged_fields, }) } } @@ -201,28 +579,191 @@ impl Default for PartitionData { leader_id: (0).into(), leader_epoch: 0, preferred_successors: Default::default(), + preferred_candidates: Default::default(), + unknown_tagged_fields: BTreeMap::new(), } } } impl Message for PartitionData { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0 +/// Valid versions: 0-1 +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct ReplicaInfo { + /// + /// + /// Supported API versions: 1 + pub candidate_id: super::BrokerId, + + /// + /// + /// Supported API versions: 1 + pub candidate_directory_id: Uuid, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, +} + +impl ReplicaInfo { + /// Sets `candidate_id` to the passed value. + /// + /// + /// + /// Supported API versions: 1 + pub fn with_candidate_id(mut self, value: super::BrokerId) -> Self { + self.candidate_id = value; + self + } + /// Sets `candidate_directory_id` to the passed value. + /// + /// + /// + /// Supported API versions: 1 + pub fn with_candidate_directory_id(mut self, value: Uuid) -> Self { + self.candidate_directory_id = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } +} + +#[cfg(feature = "client")] +impl Encodable for ReplicaInfo { + fn encode(&self, buf: &mut B, version: i16) -> Result<()> { + if version >= 1 { + types::Int32.encode(buf, &self.candidate_id)?; + } else { + if self.candidate_id != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + types::Uuid.encode(buf, &self.candidate_directory_id)?; + } else { + if &self.candidate_directory_id != &Uuid::nil() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + } + Ok(()) + } + fn compute_size(&self, version: i16) -> Result { + let mut total_size = 0; + if version >= 1 { + total_size += types::Int32.compute_size(&self.candidate_id)?; + } else { + if self.candidate_id != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + total_size += types::Uuid.compute_size(&self.candidate_directory_id)?; + } else { + if &self.candidate_directory_id != &Uuid::nil() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + } + Ok(total_size) + } +} + +#[cfg(feature = "broker")] +impl Decodable for ReplicaInfo { + fn decode(buf: &mut B, version: i16) -> Result { + let candidate_id = if version >= 1 { + types::Int32.decode(buf)? + } else { + (0).into() + }; + let candidate_directory_id = if version >= 1 { + types::Uuid.decode(buf)? + } else { + Uuid::nil() + }; + let mut unknown_tagged_fields = BTreeMap::new(); + if version >= 1 { + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } + Ok(Self { + candidate_id, + candidate_directory_id, + unknown_tagged_fields, + }) + } +} + +impl Default for ReplicaInfo { + fn default() -> Self { + Self { + candidate_id: (0).into(), + candidate_directory_id: Uuid::nil(), + unknown_tagged_fields: BTreeMap::new(), + } + } +} + +impl Message for ReplicaInfo { + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; + const DEPRECATED_VERSIONS: Option = None; +} + +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct TopicData { /// The topic name. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub topic_name: super::TopicName, /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub partitions: Vec, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, } impl TopicData { @@ -230,7 +771,7 @@ impl TopicData { /// /// The topic name. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_topic_name(mut self, value: super::TopicName) -> Self { self.topic_name = value; self @@ -239,26 +780,75 @@ impl TopicData { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_partitions(mut self, value: Vec) -> Self { self.partitions = value; self } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } } #[cfg(feature = "client")] impl Encodable for TopicData { fn encode(&self, buf: &mut B, version: i16) -> Result<()> { - types::String.encode(buf, &self.topic_name)?; - types::Array(types::Struct { version }).encode(buf, &self.partitions)?; - + if version >= 1 { + types::CompactString.encode(buf, &self.topic_name)?; + } else { + types::String.encode(buf, &self.topic_name)?; + } + if version >= 1 { + types::CompactArray(types::Struct { version }).encode(buf, &self.partitions)?; + } else { + types::Array(types::Struct { version }).encode(buf, &self.partitions)?; + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + } Ok(()) } fn compute_size(&self, version: i16) -> Result { let mut total_size = 0; - total_size += types::String.compute_size(&self.topic_name)?; - total_size += types::Array(types::Struct { version }).compute_size(&self.partitions)?; - + if version >= 1 { + total_size += types::CompactString.compute_size(&self.topic_name)?; + } else { + total_size += types::String.compute_size(&self.topic_name)?; + } + if version >= 1 { + total_size += + types::CompactArray(types::Struct { version }).compute_size(&self.partitions)?; + } else { + total_size += types::Array(types::Struct { version }).compute_size(&self.partitions)?; + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + } Ok(total_size) } } @@ -266,11 +856,30 @@ impl Encodable for TopicData { #[cfg(feature = "broker")] impl Decodable for TopicData { fn decode(buf: &mut B, version: i16) -> Result { - let topic_name = types::String.decode(buf)?; - let partitions = types::Array(types::Struct { version }).decode(buf)?; + let topic_name = if version >= 1 { + types::CompactString.decode(buf)? + } else { + types::String.decode(buf)? + }; + let partitions = if version >= 1 { + types::CompactArray(types::Struct { version }).decode(buf)? + } else { + types::Array(types::Struct { version }).decode(buf)? + }; + let mut unknown_tagged_fields = BTreeMap::new(); + if version >= 1 { + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } Ok(Self { topic_name, partitions, + unknown_tagged_fields, }) } } @@ -280,17 +889,22 @@ impl Default for TopicData { Self { topic_name: Default::default(), partitions: Default::default(), + unknown_tagged_fields: BTreeMap::new(), } } } impl Message for TopicData { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } impl HeaderVersion for EndQuorumEpochRequest { fn header_version(version: i16) -> i16 { - 1 + if version >= 1 { + 2 + } else { + 1 + } } } diff --git a/src/messages/end_quorum_epoch_response.rs b/src/messages/end_quorum_epoch_response.rs index b79850c..36cd93f 100644 --- a/src/messages/end_quorum_epoch_response.rs +++ b/src/messages/end_quorum_epoch_response.rs @@ -17,19 +17,27 @@ use crate::protocol::{ Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, }; -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct EndQuorumEpochResponse { /// The top level error code. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub error_code: i16, /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub topics: Vec, + + /// Endpoints for all leaders enumerated in PartitionData + /// + /// Supported API versions: 1 + pub node_endpoints: Vec, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, } impl EndQuorumEpochResponse { @@ -37,7 +45,7 @@ impl EndQuorumEpochResponse { /// /// The top level error code. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_error_code(mut self, value: i16) -> Self { self.error_code = value; self @@ -46,26 +54,108 @@ impl EndQuorumEpochResponse { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_topics(mut self, value: Vec) -> Self { self.topics = value; self } + /// Sets `node_endpoints` to the passed value. + /// + /// Endpoints for all leaders enumerated in PartitionData + /// + /// Supported API versions: 1 + pub fn with_node_endpoints(mut self, value: Vec) -> Self { + self.node_endpoints = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } } #[cfg(feature = "broker")] impl Encodable for EndQuorumEpochResponse { fn encode(&self, buf: &mut B, version: i16) -> Result<()> { types::Int16.encode(buf, &self.error_code)?; - types::Array(types::Struct { version }).encode(buf, &self.topics)?; - + if version >= 1 { + types::CompactArray(types::Struct { version }).encode(buf, &self.topics)?; + } else { + types::Array(types::Struct { version }).encode(buf, &self.topics)?; + } + if version >= 1 { + let mut num_tagged_fields = self.unknown_tagged_fields.len(); + if !self.node_endpoints.is_empty() { + num_tagged_fields += 1; + } + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + if !self.node_endpoints.is_empty() { + let computed_size = types::CompactArray(types::Struct { version }) + .compute_size(&self.node_endpoints)?; + if computed_size > std::u32::MAX as usize { + bail!( + "Tagged field is too large to encode ({} bytes)", + computed_size + ); + } + types::UnsignedVarInt.encode(buf, 0)?; + types::UnsignedVarInt.encode(buf, computed_size as u32)?; + types::CompactArray(types::Struct { version }).encode(buf, &self.node_endpoints)?; + } + + write_unknown_tagged_fields(buf, 1.., &self.unknown_tagged_fields)?; + } Ok(()) } fn compute_size(&self, version: i16) -> Result { let mut total_size = 0; total_size += types::Int16.compute_size(&self.error_code)?; - total_size += types::Array(types::Struct { version }).compute_size(&self.topics)?; - + if version >= 1 { + total_size += + types::CompactArray(types::Struct { version }).compute_size(&self.topics)?; + } else { + total_size += types::Array(types::Struct { version }).compute_size(&self.topics)?; + } + if version >= 1 { + let mut num_tagged_fields = self.unknown_tagged_fields.len(); + if !self.node_endpoints.is_empty() { + num_tagged_fields += 1; + } + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + if !self.node_endpoints.is_empty() { + let computed_size = types::CompactArray(types::Struct { version }) + .compute_size(&self.node_endpoints)?; + if computed_size > std::u32::MAX as usize { + bail!( + "Tagged field is too large to encode ({} bytes)", + computed_size + ); + } + total_size += types::UnsignedVarInt.compute_size(0)?; + total_size += types::UnsignedVarInt.compute_size(computed_size as u32)?; + total_size += computed_size; + } + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + } Ok(total_size) } } @@ -74,8 +164,36 @@ impl Encodable for EndQuorumEpochResponse { impl Decodable for EndQuorumEpochResponse { fn decode(buf: &mut B, version: i16) -> Result { let error_code = types::Int16.decode(buf)?; - let topics = types::Array(types::Struct { version }).decode(buf)?; - Ok(Self { error_code, topics }) + let topics = if version >= 1 { + types::CompactArray(types::Struct { version }).decode(buf)? + } else { + types::Array(types::Struct { version }).decode(buf)? + }; + let mut node_endpoints = Default::default(); + let mut unknown_tagged_fields = BTreeMap::new(); + if version >= 1 { + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + match tag { + 0 => { + node_endpoints = + types::CompactArray(types::Struct { version }).decode(buf)?; + } + _ => { + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } + } + } + Ok(Self { + error_code, + topics, + node_endpoints, + unknown_tagged_fields, + }) } } @@ -84,38 +202,236 @@ impl Default for EndQuorumEpochResponse { Self { error_code: 0, topics: Default::default(), + node_endpoints: Default::default(), + unknown_tagged_fields: BTreeMap::new(), } } } impl Message for EndQuorumEpochResponse { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0 +/// Valid versions: 0-1 +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct NodeEndpoint { + /// The ID of the associated node + /// + /// Supported API versions: 1 + pub node_id: super::BrokerId, + + /// The node's hostname + /// + /// Supported API versions: 1 + pub host: StrBytes, + + /// The node's port + /// + /// Supported API versions: 1 + pub port: u16, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, +} + +impl NodeEndpoint { + /// Sets `node_id` to the passed value. + /// + /// The ID of the associated node + /// + /// Supported API versions: 1 + pub fn with_node_id(mut self, value: super::BrokerId) -> Self { + self.node_id = value; + self + } + /// Sets `host` to the passed value. + /// + /// The node's hostname + /// + /// Supported API versions: 1 + pub fn with_host(mut self, value: StrBytes) -> Self { + self.host = value; + self + } + /// Sets `port` to the passed value. + /// + /// The node's port + /// + /// Supported API versions: 1 + pub fn with_port(mut self, value: u16) -> Self { + self.port = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } +} + +#[cfg(feature = "broker")] +impl Encodable for NodeEndpoint { + fn encode(&self, buf: &mut B, version: i16) -> Result<()> { + if version >= 1 { + types::Int32.encode(buf, &self.node_id)?; + } else { + if self.node_id != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + types::CompactString.encode(buf, &self.host)?; + } else { + if !self.host.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + types::UInt16.encode(buf, &self.port)?; + } else { + if self.port != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + } + Ok(()) + } + fn compute_size(&self, version: i16) -> Result { + let mut total_size = 0; + if version >= 1 { + total_size += types::Int32.compute_size(&self.node_id)?; + } else { + if self.node_id != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + total_size += types::CompactString.compute_size(&self.host)?; + } else { + if !self.host.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + total_size += types::UInt16.compute_size(&self.port)?; + } else { + if self.port != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + } + Ok(total_size) + } +} + +#[cfg(feature = "client")] +impl Decodable for NodeEndpoint { + fn decode(buf: &mut B, version: i16) -> Result { + let node_id = if version >= 1 { + types::Int32.decode(buf)? + } else { + (0).into() + }; + let host = if version >= 1 { + types::CompactString.decode(buf)? + } else { + Default::default() + }; + let port = if version >= 1 { + types::UInt16.decode(buf)? + } else { + 0 + }; + let mut unknown_tagged_fields = BTreeMap::new(); + if version >= 1 { + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } + Ok(Self { + node_id, + host, + port, + unknown_tagged_fields, + }) + } +} + +impl Default for NodeEndpoint { + fn default() -> Self { + Self { + node_id: (0).into(), + host: Default::default(), + port: 0, + unknown_tagged_fields: BTreeMap::new(), + } + } +} + +impl Message for NodeEndpoint { + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; + const DEPRECATED_VERSIONS: Option = None; +} + +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct PartitionData { /// The partition index. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub partition_index: i32, /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub error_code: i16, /// The ID of the current leader or -1 if the leader is unknown. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub leader_id: super::BrokerId, /// The latest known leader epoch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub leader_epoch: i32, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, } impl PartitionData { @@ -123,7 +439,7 @@ impl PartitionData { /// /// The partition index. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_partition_index(mut self, value: i32) -> Self { self.partition_index = value; self @@ -132,7 +448,7 @@ impl PartitionData { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_error_code(mut self, value: i16) -> Self { self.error_code = value; self @@ -141,7 +457,7 @@ impl PartitionData { /// /// The ID of the current leader or -1 if the leader is unknown. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_leader_id(mut self, value: super::BrokerId) -> Self { self.leader_id = value; self @@ -150,11 +466,21 @@ impl PartitionData { /// /// The latest known leader epoch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_leader_epoch(mut self, value: i32) -> Self { self.leader_epoch = value; self } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } } #[cfg(feature = "broker")] @@ -164,7 +490,18 @@ impl Encodable for PartitionData { types::Int16.encode(buf, &self.error_code)?; types::Int32.encode(buf, &self.leader_id)?; types::Int32.encode(buf, &self.leader_epoch)?; - + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + } Ok(()) } fn compute_size(&self, version: i16) -> Result { @@ -173,7 +510,18 @@ impl Encodable for PartitionData { total_size += types::Int16.compute_size(&self.error_code)?; total_size += types::Int32.compute_size(&self.leader_id)?; total_size += types::Int32.compute_size(&self.leader_epoch)?; - + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + } Ok(total_size) } } @@ -185,11 +533,22 @@ impl Decodable for PartitionData { let error_code = types::Int16.decode(buf)?; let leader_id = types::Int32.decode(buf)?; let leader_epoch = types::Int32.decode(buf)?; + let mut unknown_tagged_fields = BTreeMap::new(); + if version >= 1 { + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } Ok(Self { partition_index, error_code, leader_id, leader_epoch, + unknown_tagged_fields, }) } } @@ -201,28 +560,32 @@ impl Default for PartitionData { error_code: 0, leader_id: (0).into(), leader_epoch: 0, + unknown_tagged_fields: BTreeMap::new(), } } } impl Message for PartitionData { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct TopicData { /// The topic name. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub topic_name: super::TopicName, /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub partitions: Vec, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, } impl TopicData { @@ -230,7 +593,7 @@ impl TopicData { /// /// The topic name. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_topic_name(mut self, value: super::TopicName) -> Self { self.topic_name = value; self @@ -239,26 +602,75 @@ impl TopicData { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_partitions(mut self, value: Vec) -> Self { self.partitions = value; self } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } } #[cfg(feature = "broker")] impl Encodable for TopicData { fn encode(&self, buf: &mut B, version: i16) -> Result<()> { - types::String.encode(buf, &self.topic_name)?; - types::Array(types::Struct { version }).encode(buf, &self.partitions)?; - + if version >= 1 { + types::CompactString.encode(buf, &self.topic_name)?; + } else { + types::String.encode(buf, &self.topic_name)?; + } + if version >= 1 { + types::CompactArray(types::Struct { version }).encode(buf, &self.partitions)?; + } else { + types::Array(types::Struct { version }).encode(buf, &self.partitions)?; + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + } Ok(()) } fn compute_size(&self, version: i16) -> Result { let mut total_size = 0; - total_size += types::String.compute_size(&self.topic_name)?; - total_size += types::Array(types::Struct { version }).compute_size(&self.partitions)?; - + if version >= 1 { + total_size += types::CompactString.compute_size(&self.topic_name)?; + } else { + total_size += types::String.compute_size(&self.topic_name)?; + } + if version >= 1 { + total_size += + types::CompactArray(types::Struct { version }).compute_size(&self.partitions)?; + } else { + total_size += types::Array(types::Struct { version }).compute_size(&self.partitions)?; + } + if version >= 1 { + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + } Ok(total_size) } } @@ -266,11 +678,30 @@ impl Encodable for TopicData { #[cfg(feature = "client")] impl Decodable for TopicData { fn decode(buf: &mut B, version: i16) -> Result { - let topic_name = types::String.decode(buf)?; - let partitions = types::Array(types::Struct { version }).decode(buf)?; + let topic_name = if version >= 1 { + types::CompactString.decode(buf)? + } else { + types::String.decode(buf)? + }; + let partitions = if version >= 1 { + types::CompactArray(types::Struct { version }).decode(buf)? + } else { + types::Array(types::Struct { version }).decode(buf)? + }; + let mut unknown_tagged_fields = BTreeMap::new(); + if version >= 1 { + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } Ok(Self { topic_name, partitions, + unknown_tagged_fields, }) } } @@ -280,17 +711,22 @@ impl Default for TopicData { Self { topic_name: Default::default(), partitions: Default::default(), + unknown_tagged_fields: BTreeMap::new(), } } } impl Message for TopicData { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } impl HeaderVersion for EndQuorumEpochResponse { fn header_version(version: i16) -> i16 { - 0 + if version >= 1 { + 1 + } else { + 0 + } } } diff --git a/src/messages/fetch_request.rs b/src/messages/fetch_request.rs index 0803348..a0e4b13 100644 --- a/src/messages/fetch_request.rs +++ b/src/messages/fetch_request.rs @@ -17,40 +17,45 @@ use crate::protocol::{ Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, }; -/// Valid versions: 0-16 +/// Valid versions: 0-17 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct FetchPartition { /// The partition index. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub partition: i32, /// The current leader epoch of the partition. /// - /// Supported API versions: 9-16 + /// Supported API versions: 9-17 pub current_leader_epoch: i32, /// The message offset. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub fetch_offset: i64, /// The epoch of the last fetched record or -1 if there is none /// - /// Supported API versions: 12-16 + /// Supported API versions: 12-17 pub last_fetched_epoch: i32, /// The earliest available offset of the follower replica. The field is only used when the request is sent by the follower. /// - /// Supported API versions: 5-16 + /// Supported API versions: 5-17 pub log_start_offset: i64, /// The maximum bytes to fetch from this partition. See KIP-74 for cases where this limit may not be honored. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub partition_max_bytes: i32, + /// The directory id of the follower fetching + /// + /// Supported API versions: 17 + pub replica_directory_id: Uuid, + /// Other tagged fields pub unknown_tagged_fields: BTreeMap, } @@ -60,7 +65,7 @@ impl FetchPartition { /// /// The partition index. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub fn with_partition(mut self, value: i32) -> Self { self.partition = value; self @@ -69,7 +74,7 @@ impl FetchPartition { /// /// The current leader epoch of the partition. /// - /// Supported API versions: 9-16 + /// Supported API versions: 9-17 pub fn with_current_leader_epoch(mut self, value: i32) -> Self { self.current_leader_epoch = value; self @@ -78,7 +83,7 @@ impl FetchPartition { /// /// The message offset. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub fn with_fetch_offset(mut self, value: i64) -> Self { self.fetch_offset = value; self @@ -87,7 +92,7 @@ impl FetchPartition { /// /// The epoch of the last fetched record or -1 if there is none /// - /// Supported API versions: 12-16 + /// Supported API versions: 12-17 pub fn with_last_fetched_epoch(mut self, value: i32) -> Self { self.last_fetched_epoch = value; self @@ -96,7 +101,7 @@ impl FetchPartition { /// /// The earliest available offset of the follower replica. The field is only used when the request is sent by the follower. /// - /// Supported API versions: 5-16 + /// Supported API versions: 5-17 pub fn with_log_start_offset(mut self, value: i64) -> Self { self.log_start_offset = value; self @@ -105,11 +110,20 @@ impl FetchPartition { /// /// The maximum bytes to fetch from this partition. See KIP-74 for cases where this limit may not be honored. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub fn with_partition_max_bytes(mut self, value: i32) -> Self { self.partition_max_bytes = value; self } + /// Sets `replica_directory_id` to the passed value. + /// + /// The directory id of the follower fetching + /// + /// Supported API versions: 17 + pub fn with_replica_directory_id(mut self, value: Uuid) -> Self { + self.replica_directory_id = value; + self + } /// Sets unknown_tagged_fields to the passed value. pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { self.unknown_tagged_fields = value; @@ -142,7 +156,12 @@ impl Encodable for FetchPartition { } types::Int32.encode(buf, &self.partition_max_bytes)?; if version >= 12 { - let num_tagged_fields = self.unknown_tagged_fields.len(); + let mut num_tagged_fields = self.unknown_tagged_fields.len(); + if version >= 17 { + if &self.replica_directory_id != &Uuid::nil() { + num_tagged_fields += 1; + } + } if num_tagged_fields > std::u32::MAX as usize { bail!( "Too many tagged fields to encode ({} fields)", @@ -150,8 +169,21 @@ impl Encodable for FetchPartition { ); } types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; - - write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + if version >= 17 { + if &self.replica_directory_id != &Uuid::nil() { + let computed_size = types::Uuid.compute_size(&self.replica_directory_id)?; + if computed_size > std::u32::MAX as usize { + bail!( + "Tagged field is too large to encode ({} bytes)", + computed_size + ); + } + types::UnsignedVarInt.encode(buf, 0)?; + types::UnsignedVarInt.encode(buf, computed_size as u32)?; + types::Uuid.encode(buf, &self.replica_directory_id)?; + } + } + write_unknown_tagged_fields(buf, 1.., &self.unknown_tagged_fields)?; } Ok(()) } @@ -174,7 +206,12 @@ impl Encodable for FetchPartition { } total_size += types::Int32.compute_size(&self.partition_max_bytes)?; if version >= 12 { - let num_tagged_fields = self.unknown_tagged_fields.len(); + let mut num_tagged_fields = self.unknown_tagged_fields.len(); + if version >= 17 { + if &self.replica_directory_id != &Uuid::nil() { + num_tagged_fields += 1; + } + } if num_tagged_fields > std::u32::MAX as usize { bail!( "Too many tagged fields to encode ({} fields)", @@ -182,7 +219,20 @@ impl Encodable for FetchPartition { ); } total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; - + if version >= 17 { + if &self.replica_directory_id != &Uuid::nil() { + let computed_size = types::Uuid.compute_size(&self.replica_directory_id)?; + if computed_size > std::u32::MAX as usize { + bail!( + "Tagged field is too large to encode ({} bytes)", + computed_size + ); + } + total_size += types::UnsignedVarInt.compute_size(0)?; + total_size += types::UnsignedVarInt.compute_size(computed_size as u32)?; + total_size += computed_size; + } + } total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; } Ok(total_size) @@ -210,14 +260,26 @@ impl Decodable for FetchPartition { -1 }; let partition_max_bytes = types::Int32.decode(buf)?; + let mut replica_directory_id = Uuid::nil(); let mut unknown_tagged_fields = BTreeMap::new(); if version >= 12 { let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; for _ in 0..num_tagged_fields { let tag: u32 = types::UnsignedVarInt.decode(buf)?; let size: u32 = types::UnsignedVarInt.decode(buf)?; - let unknown_value = buf.try_get_bytes(size as usize)?; - unknown_tagged_fields.insert(tag as i32, unknown_value); + match tag { + 0 => { + if version >= 17 { + replica_directory_id = types::Uuid.decode(buf)?; + } else { + bail!("Tag {} is not valid for version {}", tag, version); + } + } + _ => { + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } } } Ok(Self { @@ -227,6 +289,7 @@ impl Decodable for FetchPartition { last_fetched_epoch, log_start_offset, partition_max_bytes, + replica_directory_id, unknown_tagged_fields, }) } @@ -241,23 +304,24 @@ impl Default for FetchPartition { last_fetched_epoch: -1, log_start_offset: -1, partition_max_bytes: 0, + replica_directory_id: Uuid::nil(), unknown_tagged_fields: BTreeMap::new(), } } } impl Message for FetchPartition { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 16 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 17 }; const DEPRECATED_VERSIONS: Option = Some(VersionRange { min: 0, max: 3 }); } -/// Valid versions: 0-16 +/// Valid versions: 0-17 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct FetchRequest { /// The clusterId if known. This is used to validate metadata fetches prior to broker registration. /// - /// Supported API versions: 12-16 + /// Supported API versions: 12-17 pub cluster_id: Option, /// The broker ID of the follower, of -1 if this request is from a consumer. @@ -267,52 +331,52 @@ pub struct FetchRequest { /// /// - /// Supported API versions: 15-16 + /// Supported API versions: 15-17 pub replica_state: ReplicaState, /// The maximum time in milliseconds to wait for the response. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub max_wait_ms: i32, /// The minimum bytes to accumulate in the response. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub min_bytes: i32, /// The maximum bytes to fetch. See KIP-74 for cases where this limit may not be honored. /// - /// Supported API versions: 3-16 + /// Supported API versions: 3-17 pub max_bytes: i32, /// This setting controls the visibility of transactional records. Using READ_UNCOMMITTED (isolation_level = 0) makes all records visible. With READ_COMMITTED (isolation_level = 1), non-transactional and COMMITTED transactional records are visible. To be more concrete, READ_COMMITTED returns all data from offsets smaller than the current LSO (last stable offset), and enables the inclusion of the list of aborted transactions in the result, which allows consumers to discard ABORTED transactional records /// - /// Supported API versions: 4-16 + /// Supported API versions: 4-17 pub isolation_level: i8, /// The fetch session ID. /// - /// Supported API versions: 7-16 + /// Supported API versions: 7-17 pub session_id: i32, /// The fetch session epoch, which is used for ordering requests in a session. /// - /// Supported API versions: 7-16 + /// Supported API versions: 7-17 pub session_epoch: i32, /// The topics to fetch. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub topics: Vec, /// In an incremental fetch request, the partitions to remove. /// - /// Supported API versions: 7-16 + /// Supported API versions: 7-17 pub forgotten_topics_data: Vec, /// Rack ID of the consumer making this request /// - /// Supported API versions: 11-16 + /// Supported API versions: 11-17 pub rack_id: StrBytes, /// Other tagged fields @@ -324,7 +388,7 @@ impl FetchRequest { /// /// The clusterId if known. This is used to validate metadata fetches prior to broker registration. /// - /// Supported API versions: 12-16 + /// Supported API versions: 12-17 pub fn with_cluster_id(mut self, value: Option) -> Self { self.cluster_id = value; self @@ -342,7 +406,7 @@ impl FetchRequest { /// /// /// - /// Supported API versions: 15-16 + /// Supported API versions: 15-17 pub fn with_replica_state(mut self, value: ReplicaState) -> Self { self.replica_state = value; self @@ -351,7 +415,7 @@ impl FetchRequest { /// /// The maximum time in milliseconds to wait for the response. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub fn with_max_wait_ms(mut self, value: i32) -> Self { self.max_wait_ms = value; self @@ -360,7 +424,7 @@ impl FetchRequest { /// /// The minimum bytes to accumulate in the response. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub fn with_min_bytes(mut self, value: i32) -> Self { self.min_bytes = value; self @@ -369,7 +433,7 @@ impl FetchRequest { /// /// The maximum bytes to fetch. See KIP-74 for cases where this limit may not be honored. /// - /// Supported API versions: 3-16 + /// Supported API versions: 3-17 pub fn with_max_bytes(mut self, value: i32) -> Self { self.max_bytes = value; self @@ -378,7 +442,7 @@ impl FetchRequest { /// /// This setting controls the visibility of transactional records. Using READ_UNCOMMITTED (isolation_level = 0) makes all records visible. With READ_COMMITTED (isolation_level = 1), non-transactional and COMMITTED transactional records are visible. To be more concrete, READ_COMMITTED returns all data from offsets smaller than the current LSO (last stable offset), and enables the inclusion of the list of aborted transactions in the result, which allows consumers to discard ABORTED transactional records /// - /// Supported API versions: 4-16 + /// Supported API versions: 4-17 pub fn with_isolation_level(mut self, value: i8) -> Self { self.isolation_level = value; self @@ -387,7 +451,7 @@ impl FetchRequest { /// /// The fetch session ID. /// - /// Supported API versions: 7-16 + /// Supported API versions: 7-17 pub fn with_session_id(mut self, value: i32) -> Self { self.session_id = value; self @@ -396,7 +460,7 @@ impl FetchRequest { /// /// The fetch session epoch, which is used for ordering requests in a session. /// - /// Supported API versions: 7-16 + /// Supported API versions: 7-17 pub fn with_session_epoch(mut self, value: i32) -> Self { self.session_epoch = value; self @@ -405,7 +469,7 @@ impl FetchRequest { /// /// The topics to fetch. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub fn with_topics(mut self, value: Vec) -> Self { self.topics = value; self @@ -414,7 +478,7 @@ impl FetchRequest { /// /// In an incremental fetch request, the partitions to remove. /// - /// Supported API versions: 7-16 + /// Supported API versions: 7-17 pub fn with_forgotten_topics_data(mut self, value: Vec) -> Self { self.forgotten_topics_data = value; self @@ -423,7 +487,7 @@ impl FetchRequest { /// /// Rack ID of the consumer making this request /// - /// Supported API versions: 11-16 + /// Supported API versions: 11-17 pub fn with_rack_id(mut self, value: StrBytes) -> Self { self.rack_id = value; self @@ -753,11 +817,11 @@ impl Default for FetchRequest { } impl Message for FetchRequest { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 16 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 17 }; const DEPRECATED_VERSIONS: Option = Some(VersionRange { min: 0, max: 3 }); } -/// Valid versions: 0-16 +/// Valid versions: 0-17 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct FetchTopic { @@ -768,12 +832,12 @@ pub struct FetchTopic { /// The unique topic ID /// - /// Supported API versions: 13-16 + /// Supported API versions: 13-17 pub topic_id: Uuid, /// The partitions to fetch. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub partitions: Vec, /// Other tagged fields @@ -794,7 +858,7 @@ impl FetchTopic { /// /// The unique topic ID /// - /// Supported API versions: 13-16 + /// Supported API versions: 13-17 pub fn with_topic_id(mut self, value: Uuid) -> Self { self.topic_id = value; self @@ -803,7 +867,7 @@ impl FetchTopic { /// /// The partitions to fetch. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub fn with_partitions(mut self, value: Vec) -> Self { self.partitions = value; self @@ -939,11 +1003,11 @@ impl Default for FetchTopic { } impl Message for FetchTopic { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 16 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 17 }; const DEPRECATED_VERSIONS: Option = Some(VersionRange { min: 0, max: 3 }); } -/// Valid versions: 0-16 +/// Valid versions: 0-17 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct ForgottenTopic { @@ -954,12 +1018,12 @@ pub struct ForgottenTopic { /// The unique topic ID /// - /// Supported API versions: 13-16 + /// Supported API versions: 13-17 pub topic_id: Uuid, /// The partitions indexes to forget. /// - /// Supported API versions: 7-16 + /// Supported API versions: 7-17 pub partitions: Vec, /// Other tagged fields @@ -980,7 +1044,7 @@ impl ForgottenTopic { /// /// The unique topic ID /// - /// Supported API versions: 13-16 + /// Supported API versions: 13-17 pub fn with_topic_id(mut self, value: Uuid) -> Self { self.topic_id = value; self @@ -989,7 +1053,7 @@ impl ForgottenTopic { /// /// The partitions indexes to forget. /// - /// Supported API versions: 7-16 + /// Supported API versions: 7-17 pub fn with_partitions(mut self, value: Vec) -> Self { self.partitions = value; self @@ -1140,22 +1204,22 @@ impl Default for ForgottenTopic { } impl Message for ForgottenTopic { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 16 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 17 }; const DEPRECATED_VERSIONS: Option = Some(VersionRange { min: 0, max: 3 }); } -/// Valid versions: 0-16 +/// Valid versions: 0-17 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct ReplicaState { /// The replica ID of the follower, or -1 if this request is from a consumer. /// - /// Supported API versions: 15-16 + /// Supported API versions: 15-17 pub replica_id: super::BrokerId, /// The epoch of this follower, or -1 if not available. /// - /// Supported API versions: 15-16 + /// Supported API versions: 15-17 pub replica_epoch: i64, /// Other tagged fields @@ -1167,7 +1231,7 @@ impl ReplicaState { /// /// The replica ID of the follower, or -1 if this request is from a consumer. /// - /// Supported API versions: 15-16 + /// Supported API versions: 15-17 pub fn with_replica_id(mut self, value: super::BrokerId) -> Self { self.replica_id = value; self @@ -1176,7 +1240,7 @@ impl ReplicaState { /// /// The epoch of this follower, or -1 if not available. /// - /// Supported API versions: 15-16 + /// Supported API versions: 15-17 pub fn with_replica_epoch(mut self, value: i64) -> Self { self.replica_epoch = value; self @@ -1298,7 +1362,7 @@ impl Default for ReplicaState { } impl Message for ReplicaState { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 16 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 17 }; const DEPRECATED_VERSIONS: Option = Some(VersionRange { min: 0, max: 3 }); } diff --git a/src/messages/fetch_response.rs b/src/messages/fetch_response.rs index 3e24afe..4e3250c 100644 --- a/src/messages/fetch_response.rs +++ b/src/messages/fetch_response.rs @@ -17,18 +17,18 @@ use crate::protocol::{ Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, }; -/// Valid versions: 0-16 +/// Valid versions: 0-17 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct AbortedTransaction { /// The producer id associated with the aborted transaction. /// - /// Supported API versions: 4-16 + /// Supported API versions: 4-17 pub producer_id: super::ProducerId, /// The first offset in the aborted transaction. /// - /// Supported API versions: 4-16 + /// Supported API versions: 4-17 pub first_offset: i64, /// Other tagged fields @@ -40,7 +40,7 @@ impl AbortedTransaction { /// /// The producer id associated with the aborted transaction. /// - /// Supported API versions: 4-16 + /// Supported API versions: 4-17 pub fn with_producer_id(mut self, value: super::ProducerId) -> Self { self.producer_id = value; self @@ -49,7 +49,7 @@ impl AbortedTransaction { /// /// The first offset in the aborted transaction. /// - /// Supported API versions: 4-16 + /// Supported API versions: 4-17 pub fn with_first_offset(mut self, value: i64) -> Self { self.first_offset = value; self @@ -171,22 +171,22 @@ impl Default for AbortedTransaction { } impl Message for AbortedTransaction { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 16 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 17 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0-16 +/// Valid versions: 0-17 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct EpochEndOffset { /// /// - /// Supported API versions: 12-16 + /// Supported API versions: 12-17 pub epoch: i32, /// /// - /// Supported API versions: 12-16 + /// Supported API versions: 12-17 pub end_offset: i64, /// Other tagged fields @@ -198,7 +198,7 @@ impl EpochEndOffset { /// /// /// - /// Supported API versions: 12-16 + /// Supported API versions: 12-17 pub fn with_epoch(mut self, value: i32) -> Self { self.epoch = value; self @@ -207,7 +207,7 @@ impl EpochEndOffset { /// /// /// - /// Supported API versions: 12-16 + /// Supported API versions: 12-17 pub fn with_end_offset(mut self, value: i64) -> Self { self.end_offset = value; self @@ -329,37 +329,37 @@ impl Default for EpochEndOffset { } impl Message for EpochEndOffset { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 16 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 17 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0-16 +/// Valid versions: 0-17 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct FetchResponse { /// The duration in milliseconds for which the request was throttled due to a quota violation, or zero if the request did not violate any quota. /// - /// Supported API versions: 1-16 + /// Supported API versions: 1-17 pub throttle_time_ms: i32, /// The top level response error code. /// - /// Supported API versions: 7-16 + /// Supported API versions: 7-17 pub error_code: i16, /// The fetch session ID, or 0 if this is not part of a fetch session. /// - /// Supported API versions: 7-16 + /// Supported API versions: 7-17 pub session_id: i32, /// The response topics. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub responses: Vec, /// Endpoints for all current-leaders enumerated in PartitionData, with errors NOT_LEADER_OR_FOLLOWER & FENCED_LEADER_EPOCH. /// - /// Supported API versions: 16 + /// Supported API versions: 16-17 pub node_endpoints: Vec, /// Other tagged fields @@ -371,7 +371,7 @@ impl FetchResponse { /// /// The duration in milliseconds for which the request was throttled due to a quota violation, or zero if the request did not violate any quota. /// - /// Supported API versions: 1-16 + /// Supported API versions: 1-17 pub fn with_throttle_time_ms(mut self, value: i32) -> Self { self.throttle_time_ms = value; self @@ -380,7 +380,7 @@ impl FetchResponse { /// /// The top level response error code. /// - /// Supported API versions: 7-16 + /// Supported API versions: 7-17 pub fn with_error_code(mut self, value: i16) -> Self { self.error_code = value; self @@ -389,7 +389,7 @@ impl FetchResponse { /// /// The fetch session ID, or 0 if this is not part of a fetch session. /// - /// Supported API versions: 7-16 + /// Supported API versions: 7-17 pub fn with_session_id(mut self, value: i32) -> Self { self.session_id = value; self @@ -398,7 +398,7 @@ impl FetchResponse { /// /// The response topics. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub fn with_responses(mut self, value: Vec) -> Self { self.responses = value; self @@ -407,7 +407,7 @@ impl FetchResponse { /// /// Endpoints for all current-leaders enumerated in PartitionData, with errors NOT_LEADER_OR_FOLLOWER & FENCED_LEADER_EPOCH. /// - /// Supported API versions: 16 + /// Supported API versions: 16-17 pub fn with_node_endpoints(mut self, value: Vec) -> Self { self.node_endpoints = value; self @@ -606,11 +606,11 @@ impl Default for FetchResponse { } impl Message for FetchResponse { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 16 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 17 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0-16 +/// Valid versions: 0-17 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct FetchableTopicResponse { @@ -621,12 +621,12 @@ pub struct FetchableTopicResponse { /// The unique topic ID /// - /// Supported API versions: 13-16 + /// Supported API versions: 13-17 pub topic_id: Uuid, /// The topic partitions. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub partitions: Vec, /// Other tagged fields @@ -647,7 +647,7 @@ impl FetchableTopicResponse { /// /// The unique topic ID /// - /// Supported API versions: 13-16 + /// Supported API versions: 13-17 pub fn with_topic_id(mut self, value: Uuid) -> Self { self.topic_id = value; self @@ -656,7 +656,7 @@ impl FetchableTopicResponse { /// /// The topic partitions. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub fn with_partitions(mut self, value: Vec) -> Self { self.partitions = value; self @@ -792,22 +792,22 @@ impl Default for FetchableTopicResponse { } impl Message for FetchableTopicResponse { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 16 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 17 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0-16 +/// Valid versions: 0-17 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct LeaderIdAndEpoch { /// The ID of the current leader or -1 if the leader is unknown. /// - /// Supported API versions: 12-16 + /// Supported API versions: 12-17 pub leader_id: super::BrokerId, /// The latest known leader epoch /// - /// Supported API versions: 12-16 + /// Supported API versions: 12-17 pub leader_epoch: i32, /// Other tagged fields @@ -819,7 +819,7 @@ impl LeaderIdAndEpoch { /// /// The ID of the current leader or -1 if the leader is unknown. /// - /// Supported API versions: 12-16 + /// Supported API versions: 12-17 pub fn with_leader_id(mut self, value: super::BrokerId) -> Self { self.leader_id = value; self @@ -828,7 +828,7 @@ impl LeaderIdAndEpoch { /// /// The latest known leader epoch /// - /// Supported API versions: 12-16 + /// Supported API versions: 12-17 pub fn with_leader_epoch(mut self, value: i32) -> Self { self.leader_epoch = value; self @@ -950,32 +950,32 @@ impl Default for LeaderIdAndEpoch { } impl Message for LeaderIdAndEpoch { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 16 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 17 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0-16 +/// Valid versions: 0-17 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct NodeEndpoint { /// The ID of the associated node. /// - /// Supported API versions: 16 + /// Supported API versions: 16-17 pub node_id: super::BrokerId, /// The node's hostname. /// - /// Supported API versions: 16 + /// Supported API versions: 16-17 pub host: StrBytes, /// The node's port. /// - /// Supported API versions: 16 + /// Supported API versions: 16-17 pub port: i32, /// The rack of the node, or null if it has not been assigned to a rack. /// - /// Supported API versions: 16 + /// Supported API versions: 16-17 pub rack: Option, /// Other tagged fields @@ -987,7 +987,7 @@ impl NodeEndpoint { /// /// The ID of the associated node. /// - /// Supported API versions: 16 + /// Supported API versions: 16-17 pub fn with_node_id(mut self, value: super::BrokerId) -> Self { self.node_id = value; self @@ -996,7 +996,7 @@ impl NodeEndpoint { /// /// The node's hostname. /// - /// Supported API versions: 16 + /// Supported API versions: 16-17 pub fn with_host(mut self, value: StrBytes) -> Self { self.host = value; self @@ -1005,7 +1005,7 @@ impl NodeEndpoint { /// /// The node's port. /// - /// Supported API versions: 16 + /// Supported API versions: 16-17 pub fn with_port(mut self, value: i32) -> Self { self.port = value; self @@ -1014,7 +1014,7 @@ impl NodeEndpoint { /// /// The rack of the node, or null if it has not been assigned to a rack. /// - /// Supported API versions: 16 + /// Supported API versions: 16-17 pub fn with_rack(mut self, value: Option) -> Self { self.rack = value; self @@ -1178,67 +1178,67 @@ impl Default for NodeEndpoint { } impl Message for NodeEndpoint { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 16 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 17 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0-16 +/// Valid versions: 0-17 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct PartitionData { /// The partition index. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub partition_index: i32, /// The error code, or 0 if there was no fetch error. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub error_code: i16, /// The current high water mark. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub high_watermark: i64, /// The last stable offset (or LSO) of the partition. This is the last offset such that the state of all transactional records prior to this offset have been decided (ABORTED or COMMITTED) /// - /// Supported API versions: 4-16 + /// Supported API versions: 4-17 pub last_stable_offset: i64, /// The current log start offset. /// - /// Supported API versions: 5-16 + /// Supported API versions: 5-17 pub log_start_offset: i64, /// In case divergence is detected based on the `LastFetchedEpoch` and `FetchOffset` in the request, this field indicates the largest epoch and its end offset such that subsequent records are known to diverge /// - /// Supported API versions: 12-16 + /// Supported API versions: 12-17 pub diverging_epoch: EpochEndOffset, /// /// - /// Supported API versions: 12-16 + /// Supported API versions: 12-17 pub current_leader: LeaderIdAndEpoch, /// In the case of fetching an offset less than the LogStartOffset, this is the end offset and epoch that should be used in the FetchSnapshot request. /// - /// Supported API versions: 12-16 + /// Supported API versions: 12-17 pub snapshot_id: SnapshotId, /// The aborted transactions. /// - /// Supported API versions: 4-16 + /// Supported API versions: 4-17 pub aborted_transactions: Option>, /// The preferred read replica for the consumer to use on its next fetch request /// - /// Supported API versions: 11-16 + /// Supported API versions: 11-17 pub preferred_read_replica: super::BrokerId, /// The record data. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub records: Option, /// Other tagged fields @@ -1250,7 +1250,7 @@ impl PartitionData { /// /// The partition index. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub fn with_partition_index(mut self, value: i32) -> Self { self.partition_index = value; self @@ -1259,7 +1259,7 @@ impl PartitionData { /// /// The error code, or 0 if there was no fetch error. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub fn with_error_code(mut self, value: i16) -> Self { self.error_code = value; self @@ -1268,7 +1268,7 @@ impl PartitionData { /// /// The current high water mark. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub fn with_high_watermark(mut self, value: i64) -> Self { self.high_watermark = value; self @@ -1277,7 +1277,7 @@ impl PartitionData { /// /// The last stable offset (or LSO) of the partition. This is the last offset such that the state of all transactional records prior to this offset have been decided (ABORTED or COMMITTED) /// - /// Supported API versions: 4-16 + /// Supported API versions: 4-17 pub fn with_last_stable_offset(mut self, value: i64) -> Self { self.last_stable_offset = value; self @@ -1286,7 +1286,7 @@ impl PartitionData { /// /// The current log start offset. /// - /// Supported API versions: 5-16 + /// Supported API versions: 5-17 pub fn with_log_start_offset(mut self, value: i64) -> Self { self.log_start_offset = value; self @@ -1295,7 +1295,7 @@ impl PartitionData { /// /// In case divergence is detected based on the `LastFetchedEpoch` and `FetchOffset` in the request, this field indicates the largest epoch and its end offset such that subsequent records are known to diverge /// - /// Supported API versions: 12-16 + /// Supported API versions: 12-17 pub fn with_diverging_epoch(mut self, value: EpochEndOffset) -> Self { self.diverging_epoch = value; self @@ -1304,7 +1304,7 @@ impl PartitionData { /// /// /// - /// Supported API versions: 12-16 + /// Supported API versions: 12-17 pub fn with_current_leader(mut self, value: LeaderIdAndEpoch) -> Self { self.current_leader = value; self @@ -1313,7 +1313,7 @@ impl PartitionData { /// /// In the case of fetching an offset less than the LogStartOffset, this is the end offset and epoch that should be used in the FetchSnapshot request. /// - /// Supported API versions: 12-16 + /// Supported API versions: 12-17 pub fn with_snapshot_id(mut self, value: SnapshotId) -> Self { self.snapshot_id = value; self @@ -1322,7 +1322,7 @@ impl PartitionData { /// /// The aborted transactions. /// - /// Supported API versions: 4-16 + /// Supported API versions: 4-17 pub fn with_aborted_transactions(mut self, value: Option>) -> Self { self.aborted_transactions = value; self @@ -1331,7 +1331,7 @@ impl PartitionData { /// /// The preferred read replica for the consumer to use on its next fetch request /// - /// Supported API versions: 11-16 + /// Supported API versions: 11-17 pub fn with_preferred_read_replica(mut self, value: super::BrokerId) -> Self { self.preferred_read_replica = value; self @@ -1340,7 +1340,7 @@ impl PartitionData { /// /// The record data. /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub fn with_records(mut self, value: Option) -> Self { self.records = value; self @@ -1641,22 +1641,22 @@ impl Default for PartitionData { } impl Message for PartitionData { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 16 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 17 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0-16 +/// Valid versions: 0-17 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct SnapshotId { /// /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub end_offset: i64, /// /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub epoch: i32, /// Other tagged fields @@ -1668,7 +1668,7 @@ impl SnapshotId { /// /// /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub fn with_end_offset(mut self, value: i64) -> Self { self.end_offset = value; self @@ -1677,7 +1677,7 @@ impl SnapshotId { /// /// /// - /// Supported API versions: 0-16 + /// Supported API versions: 0-17 pub fn with_epoch(mut self, value: i32) -> Self { self.epoch = value; self @@ -1767,7 +1767,7 @@ impl Default for SnapshotId { } impl Message for SnapshotId { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 16 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 17 }; const DEPRECATED_VERSIONS: Option = None; } diff --git a/src/messages/fetch_snapshot_request.rs b/src/messages/fetch_snapshot_request.rs index b99f0f3..01bb2c1 100644 --- a/src/messages/fetch_snapshot_request.rs +++ b/src/messages/fetch_snapshot_request.rs @@ -17,28 +17,28 @@ use crate::protocol::{ Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, }; -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct FetchSnapshotRequest { /// The clusterId if known, this is used to validate metadata fetches prior to broker registration /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub cluster_id: Option, /// The broker ID of the follower /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub replica_id: super::BrokerId, /// The maximum bytes to fetch from all of the snapshots /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub max_bytes: i32, /// The topics to fetch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub topics: Vec, /// Other tagged fields @@ -50,7 +50,7 @@ impl FetchSnapshotRequest { /// /// The clusterId if known, this is used to validate metadata fetches prior to broker registration /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_cluster_id(mut self, value: Option) -> Self { self.cluster_id = value; self @@ -59,7 +59,7 @@ impl FetchSnapshotRequest { /// /// The broker ID of the follower /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_replica_id(mut self, value: super::BrokerId) -> Self { self.replica_id = value; self @@ -68,7 +68,7 @@ impl FetchSnapshotRequest { /// /// The maximum bytes to fetch from all of the snapshots /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_max_bytes(mut self, value: i32) -> Self { self.max_bytes = value; self @@ -77,7 +77,7 @@ impl FetchSnapshotRequest { /// /// The topics to fetch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_topics(mut self, value: Vec) -> Self { self.topics = value; self @@ -206,34 +206,39 @@ impl Default for FetchSnapshotRequest { } impl Message for FetchSnapshotRequest { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct PartitionSnapshot { /// The partition index /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub partition: i32, /// The current leader epoch of the partition, -1 for unknown leader epoch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub current_leader_epoch: i32, /// The snapshot endOffset and epoch to fetch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub snapshot_id: SnapshotId, /// The byte position within the snapshot to start fetching from /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub position: i64, + /// The directory id of the follower fetching + /// + /// Supported API versions: 1 + pub replica_directory_id: Uuid, + /// Other tagged fields pub unknown_tagged_fields: BTreeMap, } @@ -243,7 +248,7 @@ impl PartitionSnapshot { /// /// The partition index /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_partition(mut self, value: i32) -> Self { self.partition = value; self @@ -252,7 +257,7 @@ impl PartitionSnapshot { /// /// The current leader epoch of the partition, -1 for unknown leader epoch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_current_leader_epoch(mut self, value: i32) -> Self { self.current_leader_epoch = value; self @@ -261,7 +266,7 @@ impl PartitionSnapshot { /// /// The snapshot endOffset and epoch to fetch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_snapshot_id(mut self, value: SnapshotId) -> Self { self.snapshot_id = value; self @@ -270,11 +275,20 @@ impl PartitionSnapshot { /// /// The byte position within the snapshot to start fetching from /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_position(mut self, value: i64) -> Self { self.position = value; self } + /// Sets `replica_directory_id` to the passed value. + /// + /// The directory id of the follower fetching + /// + /// Supported API versions: 1 + pub fn with_replica_directory_id(mut self, value: Uuid) -> Self { + self.replica_directory_id = value; + self + } /// Sets unknown_tagged_fields to the passed value. pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { self.unknown_tagged_fields = value; @@ -294,7 +308,12 @@ impl Encodable for PartitionSnapshot { types::Int32.encode(buf, &self.current_leader_epoch)?; types::Struct { version }.encode(buf, &self.snapshot_id)?; types::Int64.encode(buf, &self.position)?; - let num_tagged_fields = self.unknown_tagged_fields.len(); + let mut num_tagged_fields = self.unknown_tagged_fields.len(); + if version >= 1 { + if &self.replica_directory_id != &Uuid::nil() { + num_tagged_fields += 1; + } + } if num_tagged_fields > std::u32::MAX as usize { bail!( "Too many tagged fields to encode ({} fields)", @@ -302,8 +321,21 @@ impl Encodable for PartitionSnapshot { ); } types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; - - write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + if version >= 1 { + if &self.replica_directory_id != &Uuid::nil() { + let computed_size = types::Uuid.compute_size(&self.replica_directory_id)?; + if computed_size > std::u32::MAX as usize { + bail!( + "Tagged field is too large to encode ({} bytes)", + computed_size + ); + } + types::UnsignedVarInt.encode(buf, 0)?; + types::UnsignedVarInt.encode(buf, computed_size as u32)?; + types::Uuid.encode(buf, &self.replica_directory_id)?; + } + } + write_unknown_tagged_fields(buf, 1.., &self.unknown_tagged_fields)?; Ok(()) } fn compute_size(&self, version: i16) -> Result { @@ -312,7 +344,12 @@ impl Encodable for PartitionSnapshot { total_size += types::Int32.compute_size(&self.current_leader_epoch)?; total_size += types::Struct { version }.compute_size(&self.snapshot_id)?; total_size += types::Int64.compute_size(&self.position)?; - let num_tagged_fields = self.unknown_tagged_fields.len(); + let mut num_tagged_fields = self.unknown_tagged_fields.len(); + if version >= 1 { + if &self.replica_directory_id != &Uuid::nil() { + num_tagged_fields += 1; + } + } if num_tagged_fields > std::u32::MAX as usize { bail!( "Too many tagged fields to encode ({} fields)", @@ -320,7 +357,20 @@ impl Encodable for PartitionSnapshot { ); } total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; - + if version >= 1 { + if &self.replica_directory_id != &Uuid::nil() { + let computed_size = types::Uuid.compute_size(&self.replica_directory_id)?; + if computed_size > std::u32::MAX as usize { + bail!( + "Tagged field is too large to encode ({} bytes)", + computed_size + ); + } + total_size += types::UnsignedVarInt.compute_size(0)?; + total_size += types::UnsignedVarInt.compute_size(computed_size as u32)?; + total_size += computed_size; + } + } total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; Ok(total_size) } @@ -333,19 +383,32 @@ impl Decodable for PartitionSnapshot { let current_leader_epoch = types::Int32.decode(buf)?; let snapshot_id = types::Struct { version }.decode(buf)?; let position = types::Int64.decode(buf)?; + let mut replica_directory_id = Uuid::nil(); let mut unknown_tagged_fields = BTreeMap::new(); let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; for _ in 0..num_tagged_fields { let tag: u32 = types::UnsignedVarInt.decode(buf)?; let size: u32 = types::UnsignedVarInt.decode(buf)?; - let unknown_value = buf.try_get_bytes(size as usize)?; - unknown_tagged_fields.insert(tag as i32, unknown_value); + match tag { + 0 => { + if version >= 1 { + replica_directory_id = types::Uuid.decode(buf)?; + } else { + bail!("Tag {} is not valid for version {}", tag, version); + } + } + _ => { + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } } Ok(Self { partition, current_leader_epoch, snapshot_id, position, + replica_directory_id, unknown_tagged_fields, }) } @@ -358,28 +421,29 @@ impl Default for PartitionSnapshot { current_leader_epoch: 0, snapshot_id: Default::default(), position: 0, + replica_directory_id: Uuid::nil(), unknown_tagged_fields: BTreeMap::new(), } } } impl Message for PartitionSnapshot { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct SnapshotId { /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub end_offset: i64, /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub epoch: i32, /// Other tagged fields @@ -391,7 +455,7 @@ impl SnapshotId { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_end_offset(mut self, value: i64) -> Self { self.end_offset = value; self @@ -400,7 +464,7 @@ impl SnapshotId { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_epoch(mut self, value: i32) -> Self { self.epoch = value; self @@ -484,22 +548,22 @@ impl Default for SnapshotId { } impl Message for SnapshotId { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct TopicSnapshot { /// The name of the topic to fetch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub name: super::TopicName, /// The partitions to fetch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub partitions: Vec, /// Other tagged fields @@ -511,7 +575,7 @@ impl TopicSnapshot { /// /// The name of the topic to fetch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_name(mut self, value: super::TopicName) -> Self { self.name = value; self @@ -520,7 +584,7 @@ impl TopicSnapshot { /// /// The partitions to fetch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_partitions(mut self, value: Vec) -> Self { self.partitions = value; self @@ -605,7 +669,7 @@ impl Default for TopicSnapshot { } impl Message for TopicSnapshot { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } diff --git a/src/messages/fetch_snapshot_response.rs b/src/messages/fetch_snapshot_response.rs index c4d8976..393c999 100644 --- a/src/messages/fetch_snapshot_response.rs +++ b/src/messages/fetch_snapshot_response.rs @@ -17,25 +17,30 @@ use crate::protocol::{ Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, }; -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct FetchSnapshotResponse { /// The duration in milliseconds for which the request was throttled due to a quota violation, or zero if the request did not violate any quota. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub throttle_time_ms: i32, /// The top level response error code. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub error_code: i16, /// The topics to fetch. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub topics: Vec, + /// Endpoints for all current-leaders enumerated in PartitionSnapshot + /// + /// Supported API versions: 1 + pub node_endpoints: Vec, + /// Other tagged fields pub unknown_tagged_fields: BTreeMap, } @@ -45,7 +50,7 @@ impl FetchSnapshotResponse { /// /// The duration in milliseconds for which the request was throttled due to a quota violation, or zero if the request did not violate any quota. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_throttle_time_ms(mut self, value: i32) -> Self { self.throttle_time_ms = value; self @@ -54,7 +59,7 @@ impl FetchSnapshotResponse { /// /// The top level response error code. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_error_code(mut self, value: i16) -> Self { self.error_code = value; self @@ -63,11 +68,20 @@ impl FetchSnapshotResponse { /// /// The topics to fetch. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_topics(mut self, value: Vec) -> Self { self.topics = value; self } + /// Sets `node_endpoints` to the passed value. + /// + /// Endpoints for all current-leaders enumerated in PartitionSnapshot + /// + /// Supported API versions: 1 + pub fn with_node_endpoints(mut self, value: Vec) -> Self { + self.node_endpoints = value; + self + } /// Sets unknown_tagged_fields to the passed value. pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { self.unknown_tagged_fields = value; @@ -86,7 +100,12 @@ impl Encodable for FetchSnapshotResponse { types::Int32.encode(buf, &self.throttle_time_ms)?; types::Int16.encode(buf, &self.error_code)?; types::CompactArray(types::Struct { version }).encode(buf, &self.topics)?; - let num_tagged_fields = self.unknown_tagged_fields.len(); + let mut num_tagged_fields = self.unknown_tagged_fields.len(); + if version >= 1 { + if !self.node_endpoints.is_empty() { + num_tagged_fields += 1; + } + } if num_tagged_fields > std::u32::MAX as usize { bail!( "Too many tagged fields to encode ({} fields)", @@ -94,8 +113,22 @@ impl Encodable for FetchSnapshotResponse { ); } types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; - - write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + if version >= 1 { + if !self.node_endpoints.is_empty() { + let computed_size = types::CompactArray(types::Struct { version }) + .compute_size(&self.node_endpoints)?; + if computed_size > std::u32::MAX as usize { + bail!( + "Tagged field is too large to encode ({} bytes)", + computed_size + ); + } + types::UnsignedVarInt.encode(buf, 0)?; + types::UnsignedVarInt.encode(buf, computed_size as u32)?; + types::CompactArray(types::Struct { version }).encode(buf, &self.node_endpoints)?; + } + } + write_unknown_tagged_fields(buf, 1.., &self.unknown_tagged_fields)?; Ok(()) } fn compute_size(&self, version: i16) -> Result { @@ -103,7 +136,12 @@ impl Encodable for FetchSnapshotResponse { total_size += types::Int32.compute_size(&self.throttle_time_ms)?; total_size += types::Int16.compute_size(&self.error_code)?; total_size += types::CompactArray(types::Struct { version }).compute_size(&self.topics)?; - let num_tagged_fields = self.unknown_tagged_fields.len(); + let mut num_tagged_fields = self.unknown_tagged_fields.len(); + if version >= 1 { + if !self.node_endpoints.is_empty() { + num_tagged_fields += 1; + } + } if num_tagged_fields > std::u32::MAX as usize { bail!( "Too many tagged fields to encode ({} fields)", @@ -111,7 +149,21 @@ impl Encodable for FetchSnapshotResponse { ); } total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; - + if version >= 1 { + if !self.node_endpoints.is_empty() { + let computed_size = types::CompactArray(types::Struct { version }) + .compute_size(&self.node_endpoints)?; + if computed_size > std::u32::MAX as usize { + bail!( + "Tagged field is too large to encode ({} bytes)", + computed_size + ); + } + total_size += types::UnsignedVarInt.compute_size(0)?; + total_size += types::UnsignedVarInt.compute_size(computed_size as u32)?; + total_size += computed_size; + } + } total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; Ok(total_size) } @@ -123,18 +175,32 @@ impl Decodable for FetchSnapshotResponse { let throttle_time_ms = types::Int32.decode(buf)?; let error_code = types::Int16.decode(buf)?; let topics = types::CompactArray(types::Struct { version }).decode(buf)?; + let mut node_endpoints = Default::default(); let mut unknown_tagged_fields = BTreeMap::new(); let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; for _ in 0..num_tagged_fields { let tag: u32 = types::UnsignedVarInt.decode(buf)?; let size: u32 = types::UnsignedVarInt.decode(buf)?; - let unknown_value = buf.try_get_bytes(size as usize)?; - unknown_tagged_fields.insert(tag as i32, unknown_value); + match tag { + 0 => { + if version >= 1 { + node_endpoints = + types::CompactArray(types::Struct { version }).decode(buf)?; + } else { + bail!("Tag {} is not valid for version {}", tag, version); + } + } + _ => { + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } } Ok(Self { throttle_time_ms, error_code, topics, + node_endpoints, unknown_tagged_fields, }) } @@ -146,28 +212,29 @@ impl Default for FetchSnapshotResponse { throttle_time_ms: 0, error_code: 0, topics: Default::default(), + node_endpoints: Default::default(), unknown_tagged_fields: BTreeMap::new(), } } } impl Message for FetchSnapshotResponse { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct LeaderIdAndEpoch { /// The ID of the current leader or -1 if the leader is unknown. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub leader_id: super::BrokerId, /// The latest known leader epoch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub leader_epoch: i32, /// Other tagged fields @@ -179,7 +246,7 @@ impl LeaderIdAndEpoch { /// /// The ID of the current leader or -1 if the leader is unknown. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_leader_id(mut self, value: super::BrokerId) -> Self { self.leader_id = value; self @@ -188,7 +255,7 @@ impl LeaderIdAndEpoch { /// /// The latest known leader epoch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_leader_epoch(mut self, value: i32) -> Self { self.leader_epoch = value; self @@ -272,47 +339,234 @@ impl Default for LeaderIdAndEpoch { } impl Message for LeaderIdAndEpoch { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; + const DEPRECATED_VERSIONS: Option = None; +} + +/// Valid versions: 0-1 +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct NodeEndpoint { + /// The ID of the associated node + /// + /// Supported API versions: 1 + pub node_id: super::BrokerId, + + /// The node's hostname + /// + /// Supported API versions: 1 + pub host: StrBytes, + + /// The node's port + /// + /// Supported API versions: 1 + pub port: u16, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, +} + +impl NodeEndpoint { + /// Sets `node_id` to the passed value. + /// + /// The ID of the associated node + /// + /// Supported API versions: 1 + pub fn with_node_id(mut self, value: super::BrokerId) -> Self { + self.node_id = value; + self + } + /// Sets `host` to the passed value. + /// + /// The node's hostname + /// + /// Supported API versions: 1 + pub fn with_host(mut self, value: StrBytes) -> Self { + self.host = value; + self + } + /// Sets `port` to the passed value. + /// + /// The node's port + /// + /// Supported API versions: 1 + pub fn with_port(mut self, value: u16) -> Self { + self.port = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } +} + +#[cfg(feature = "broker")] +impl Encodable for NodeEndpoint { + fn encode(&self, buf: &mut B, version: i16) -> Result<()> { + if version >= 1 { + types::Int32.encode(buf, &self.node_id)?; + } else { + if self.node_id != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + types::CompactString.encode(buf, &self.host)?; + } else { + if !self.host.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + types::UInt16.encode(buf, &self.port)?; + } else { + if self.port != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + Ok(()) + } + fn compute_size(&self, version: i16) -> Result { + let mut total_size = 0; + if version >= 1 { + total_size += types::Int32.compute_size(&self.node_id)?; + } else { + if self.node_id != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + total_size += types::CompactString.compute_size(&self.host)?; + } else { + if !self.host.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + total_size += types::UInt16.compute_size(&self.port)?; + } else { + if self.port != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + Ok(total_size) + } +} + +#[cfg(feature = "client")] +impl Decodable for NodeEndpoint { + fn decode(buf: &mut B, version: i16) -> Result { + let node_id = if version >= 1 { + types::Int32.decode(buf)? + } else { + (0).into() + }; + let host = if version >= 1 { + types::CompactString.decode(buf)? + } else { + Default::default() + }; + let port = if version >= 1 { + types::UInt16.decode(buf)? + } else { + 0 + }; + let mut unknown_tagged_fields = BTreeMap::new(); + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + Ok(Self { + node_id, + host, + port, + unknown_tagged_fields, + }) + } +} + +impl Default for NodeEndpoint { + fn default() -> Self { + Self { + node_id: (0).into(), + host: Default::default(), + port: 0, + unknown_tagged_fields: BTreeMap::new(), + } + } +} + +impl Message for NodeEndpoint { + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct PartitionSnapshot { /// The partition index. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub index: i32, /// The error code, or 0 if there was no fetch error. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub error_code: i16, /// The snapshot endOffset and epoch fetched /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub snapshot_id: SnapshotId, /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub current_leader: LeaderIdAndEpoch, /// The total size of the snapshot. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub size: i64, /// The starting byte position within the snapshot included in the Bytes field. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub position: i64, /// Snapshot data in records format which may not be aligned on an offset boundary /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub unaligned_records: Bytes, /// Other tagged fields @@ -324,7 +578,7 @@ impl PartitionSnapshot { /// /// The partition index. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_index(mut self, value: i32) -> Self { self.index = value; self @@ -333,7 +587,7 @@ impl PartitionSnapshot { /// /// The error code, or 0 if there was no fetch error. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_error_code(mut self, value: i16) -> Self { self.error_code = value; self @@ -342,7 +596,7 @@ impl PartitionSnapshot { /// /// The snapshot endOffset and epoch fetched /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_snapshot_id(mut self, value: SnapshotId) -> Self { self.snapshot_id = value; self @@ -351,7 +605,7 @@ impl PartitionSnapshot { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_current_leader(mut self, value: LeaderIdAndEpoch) -> Self { self.current_leader = value; self @@ -360,7 +614,7 @@ impl PartitionSnapshot { /// /// The total size of the snapshot. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_size(mut self, value: i64) -> Self { self.size = value; self @@ -369,7 +623,7 @@ impl PartitionSnapshot { /// /// The starting byte position within the snapshot included in the Bytes field. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_position(mut self, value: i64) -> Self { self.position = value; self @@ -378,7 +632,7 @@ impl PartitionSnapshot { /// /// Snapshot data in records format which may not be aligned on an offset boundary /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_unaligned_records(mut self, value: Bytes) -> Self { self.unaligned_records = value; self @@ -522,22 +776,22 @@ impl Default for PartitionSnapshot { } impl Message for PartitionSnapshot { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct SnapshotId { /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub end_offset: i64, /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub epoch: i32, /// Other tagged fields @@ -549,7 +803,7 @@ impl SnapshotId { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_end_offset(mut self, value: i64) -> Self { self.end_offset = value; self @@ -558,7 +812,7 @@ impl SnapshotId { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_epoch(mut self, value: i32) -> Self { self.epoch = value; self @@ -642,22 +896,22 @@ impl Default for SnapshotId { } impl Message for SnapshotId { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct TopicSnapshot { /// The name of the topic to fetch. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub name: super::TopicName, /// The partitions to fetch. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub partitions: Vec, /// Other tagged fields @@ -669,7 +923,7 @@ impl TopicSnapshot { /// /// The name of the topic to fetch. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_name(mut self, value: super::TopicName) -> Self { self.name = value; self @@ -678,7 +932,7 @@ impl TopicSnapshot { /// /// The partitions to fetch. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_partitions(mut self, value: Vec) -> Self { self.partitions = value; self @@ -763,7 +1017,7 @@ impl Default for TopicSnapshot { } impl Message for TopicSnapshot { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } diff --git a/src/messages/find_coordinator_request.rs b/src/messages/find_coordinator_request.rs index 844b61c..7820923 100644 --- a/src/messages/find_coordinator_request.rs +++ b/src/messages/find_coordinator_request.rs @@ -17,7 +17,7 @@ use crate::protocol::{ Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, }; -/// Valid versions: 0-5 +/// Valid versions: 0-6 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct FindCoordinatorRequest { @@ -28,12 +28,12 @@ pub struct FindCoordinatorRequest { /// The coordinator key type. (Group, transaction, etc.) /// - /// Supported API versions: 1-5 + /// Supported API versions: 1-6 pub key_type: i8, /// The coordinator keys. /// - /// Supported API versions: 4-5 + /// Supported API versions: 4-6 pub coordinator_keys: Vec, /// Other tagged fields @@ -54,7 +54,7 @@ impl FindCoordinatorRequest { /// /// The coordinator key type. (Group, transaction, etc.) /// - /// Supported API versions: 1-5 + /// Supported API versions: 1-6 pub fn with_key_type(mut self, value: i8) -> Self { self.key_type = value; self @@ -63,7 +63,7 @@ impl FindCoordinatorRequest { /// /// The coordinator keys. /// - /// Supported API versions: 4-5 + /// Supported API versions: 4-6 pub fn with_coordinator_keys(mut self, value: Vec) -> Self { self.coordinator_keys = value; self @@ -219,7 +219,7 @@ impl Default for FindCoordinatorRequest { } impl Message for FindCoordinatorRequest { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 5 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 6 }; const DEPRECATED_VERSIONS: Option = Some(VersionRange { min: 0, max: 0 }); } diff --git a/src/messages/find_coordinator_response.rs b/src/messages/find_coordinator_response.rs index 3ba54cb..c4270d4 100644 --- a/src/messages/find_coordinator_response.rs +++ b/src/messages/find_coordinator_response.rs @@ -17,38 +17,38 @@ use crate::protocol::{ Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, }; -/// Valid versions: 0-5 +/// Valid versions: 0-6 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct Coordinator { /// The coordinator key. /// - /// Supported API versions: 4-5 + /// Supported API versions: 4-6 pub key: StrBytes, /// The node id. /// - /// Supported API versions: 4-5 + /// Supported API versions: 4-6 pub node_id: super::BrokerId, /// The host name. /// - /// Supported API versions: 4-5 + /// Supported API versions: 4-6 pub host: StrBytes, /// The port. /// - /// Supported API versions: 4-5 + /// Supported API versions: 4-6 pub port: i32, /// The error code, or 0 if there was no error. /// - /// Supported API versions: 4-5 + /// Supported API versions: 4-6 pub error_code: i16, /// The error message, or null if there was no error. /// - /// Supported API versions: 4-5 + /// Supported API versions: 4-6 pub error_message: Option, /// Other tagged fields @@ -60,7 +60,7 @@ impl Coordinator { /// /// The coordinator key. /// - /// Supported API versions: 4-5 + /// Supported API versions: 4-6 pub fn with_key(mut self, value: StrBytes) -> Self { self.key = value; self @@ -69,7 +69,7 @@ impl Coordinator { /// /// The node id. /// - /// Supported API versions: 4-5 + /// Supported API versions: 4-6 pub fn with_node_id(mut self, value: super::BrokerId) -> Self { self.node_id = value; self @@ -78,7 +78,7 @@ impl Coordinator { /// /// The host name. /// - /// Supported API versions: 4-5 + /// Supported API versions: 4-6 pub fn with_host(mut self, value: StrBytes) -> Self { self.host = value; self @@ -87,7 +87,7 @@ impl Coordinator { /// /// The port. /// - /// Supported API versions: 4-5 + /// Supported API versions: 4-6 pub fn with_port(mut self, value: i32) -> Self { self.port = value; self @@ -96,7 +96,7 @@ impl Coordinator { /// /// The error code, or 0 if there was no error. /// - /// Supported API versions: 4-5 + /// Supported API versions: 4-6 pub fn with_error_code(mut self, value: i16) -> Self { self.error_code = value; self @@ -105,7 +105,7 @@ impl Coordinator { /// /// The error message, or null if there was no error. /// - /// Supported API versions: 4-5 + /// Supported API versions: 4-6 pub fn with_error_message(mut self, value: Option) -> Self { self.error_message = value; self @@ -303,17 +303,17 @@ impl Default for Coordinator { } impl Message for Coordinator { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 5 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 6 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0-5 +/// Valid versions: 0-6 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct FindCoordinatorResponse { /// The duration in milliseconds for which the request was throttled due to a quota violation, or zero if the request did not violate any quota. /// - /// Supported API versions: 1-5 + /// Supported API versions: 1-6 pub throttle_time_ms: i32, /// The error code, or 0 if there was no error. @@ -343,7 +343,7 @@ pub struct FindCoordinatorResponse { /// Each coordinator result in the response /// - /// Supported API versions: 4-5 + /// Supported API versions: 4-6 pub coordinators: Vec, /// Other tagged fields @@ -355,7 +355,7 @@ impl FindCoordinatorResponse { /// /// The duration in milliseconds for which the request was throttled due to a quota violation, or zero if the request did not violate any quota. /// - /// Supported API versions: 1-5 + /// Supported API versions: 1-6 pub fn with_throttle_time_ms(mut self, value: i32) -> Self { self.throttle_time_ms = value; self @@ -409,7 +409,7 @@ impl FindCoordinatorResponse { /// /// Each coordinator result in the response /// - /// Supported API versions: 4-5 + /// Supported API versions: 4-6 pub fn with_coordinators(mut self, value: Vec) -> Self { self.coordinators = value; self @@ -645,7 +645,7 @@ impl Default for FindCoordinatorResponse { } impl Message for FindCoordinatorResponse { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 5 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 6 }; const DEPRECATED_VERSIONS: Option = None; } diff --git a/src/messages/leader_change_message.rs b/src/messages/leader_change_message.rs index c889606..ab48d58 100644 --- a/src/messages/leader_change_message.rs +++ b/src/messages/leader_change_message.rs @@ -17,28 +17,28 @@ use crate::protocol::{ Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, }; -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct LeaderChangeMessage { /// The version of the leader change message /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub version: i16, /// The ID of the newly elected leader /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub leader_id: super::BrokerId, /// The set of voters in the quorum for this epoch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub voters: Vec, /// The voters who voted for the leader at the time of election /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub granting_voters: Vec, /// Other tagged fields @@ -50,7 +50,7 @@ impl LeaderChangeMessage { /// /// The version of the leader change message /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_version(mut self, value: i16) -> Self { self.version = value; self @@ -59,7 +59,7 @@ impl LeaderChangeMessage { /// /// The ID of the newly elected leader /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_leader_id(mut self, value: super::BrokerId) -> Self { self.leader_id = value; self @@ -68,7 +68,7 @@ impl LeaderChangeMessage { /// /// The set of voters in the quorum for this epoch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_voters(mut self, value: Vec) -> Self { self.voters = value; self @@ -77,7 +77,7 @@ impl LeaderChangeMessage { /// /// The voters who voted for the leader at the time of election /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_granting_voters(mut self, value: Vec) -> Self { self.granting_voters = value; self @@ -170,19 +170,24 @@ impl Default for LeaderChangeMessage { } impl Message for LeaderChangeMessage { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct Voter { /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub voter_id: i32, + /// The directory id of the voter + /// + /// Supported API versions: 1 + pub voter_directory_id: Uuid, + /// Other tagged fields pub unknown_tagged_fields: BTreeMap, } @@ -192,11 +197,20 @@ impl Voter { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_voter_id(mut self, value: i32) -> Self { self.voter_id = value; self } + /// Sets `voter_directory_id` to the passed value. + /// + /// The directory id of the voter + /// + /// Supported API versions: 1 + pub fn with_voter_directory_id(mut self, value: Uuid) -> Self { + self.voter_directory_id = value; + self + } /// Sets unknown_tagged_fields to the passed value. pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { self.unknown_tagged_fields = value; @@ -212,6 +226,13 @@ impl Voter { impl Encodable for Voter { fn encode(&self, buf: &mut B, version: i16) -> Result<()> { types::Int32.encode(buf, &self.voter_id)?; + if version >= 1 { + types::Uuid.encode(buf, &self.voter_directory_id)?; + } else { + if &self.voter_directory_id != &Uuid::nil() { + bail!("A field is set that is not available on the selected protocol version"); + } + } let num_tagged_fields = self.unknown_tagged_fields.len(); if num_tagged_fields > std::u32::MAX as usize { bail!( @@ -227,6 +248,13 @@ impl Encodable for Voter { fn compute_size(&self, version: i16) -> Result { let mut total_size = 0; total_size += types::Int32.compute_size(&self.voter_id)?; + if version >= 1 { + total_size += types::Uuid.compute_size(&self.voter_directory_id)?; + } else { + if &self.voter_directory_id != &Uuid::nil() { + bail!("A field is set that is not available on the selected protocol version"); + } + } let num_tagged_fields = self.unknown_tagged_fields.len(); if num_tagged_fields > std::u32::MAX as usize { bail!( @@ -244,6 +272,11 @@ impl Encodable for Voter { impl Decodable for Voter { fn decode(buf: &mut B, version: i16) -> Result { let voter_id = types::Int32.decode(buf)?; + let voter_directory_id = if version >= 1 { + types::Uuid.decode(buf)? + } else { + Uuid::nil() + }; let mut unknown_tagged_fields = BTreeMap::new(); let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; for _ in 0..num_tagged_fields { @@ -254,6 +287,7 @@ impl Decodable for Voter { } Ok(Self { voter_id, + voter_directory_id, unknown_tagged_fields, }) } @@ -263,12 +297,13 @@ impl Default for Voter { fn default() -> Self { Self { voter_id: 0, + voter_directory_id: Uuid::nil(), unknown_tagged_fields: BTreeMap::new(), } } } impl Message for Voter { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } diff --git a/src/messages/list_offsets_request.rs b/src/messages/list_offsets_request.rs index 62e1685..249fdb3 100644 --- a/src/messages/list_offsets_request.rs +++ b/src/messages/list_offsets_request.rs @@ -17,23 +17,23 @@ use crate::protocol::{ Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, }; -/// Valid versions: 0-8 +/// Valid versions: 0-9 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct ListOffsetsPartition { /// The partition index. /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub partition_index: i32, /// The current leader epoch. /// - /// Supported API versions: 4-8 + /// Supported API versions: 4-9 pub current_leader_epoch: i32, /// The current timestamp. /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub timestamp: i64, /// The maximum number of offsets to report. @@ -50,7 +50,7 @@ impl ListOffsetsPartition { /// /// The partition index. /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub fn with_partition_index(mut self, value: i32) -> Self { self.partition_index = value; self @@ -59,7 +59,7 @@ impl ListOffsetsPartition { /// /// The current leader epoch. /// - /// Supported API versions: 4-8 + /// Supported API versions: 4-9 pub fn with_current_leader_epoch(mut self, value: i32) -> Self { self.current_leader_epoch = value; self @@ -68,7 +68,7 @@ impl ListOffsetsPartition { /// /// The current timestamp. /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub fn with_timestamp(mut self, value: i64) -> Self { self.timestamp = value; self @@ -201,27 +201,27 @@ impl Default for ListOffsetsPartition { } impl Message for ListOffsetsPartition { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 8 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 9 }; const DEPRECATED_VERSIONS: Option = Some(VersionRange { min: 0, max: 0 }); } -/// Valid versions: 0-8 +/// Valid versions: 0-9 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct ListOffsetsRequest { /// The broker ID of the requester, or -1 if this request is being made by a normal consumer. /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub replica_id: super::BrokerId, /// This setting controls the visibility of transactional records. Using READ_UNCOMMITTED (isolation_level = 0) makes all records visible. With READ_COMMITTED (isolation_level = 1), non-transactional and COMMITTED transactional records are visible. To be more concrete, READ_COMMITTED returns all data from offsets smaller than the current LSO (last stable offset), and enables the inclusion of the list of aborted transactions in the result, which allows consumers to discard ABORTED transactional records /// - /// Supported API versions: 2-8 + /// Supported API versions: 2-9 pub isolation_level: i8, /// Each topic in the request. /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub topics: Vec, /// Other tagged fields @@ -233,7 +233,7 @@ impl ListOffsetsRequest { /// /// The broker ID of the requester, or -1 if this request is being made by a normal consumer. /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub fn with_replica_id(mut self, value: super::BrokerId) -> Self { self.replica_id = value; self @@ -242,7 +242,7 @@ impl ListOffsetsRequest { /// /// This setting controls the visibility of transactional records. Using READ_UNCOMMITTED (isolation_level = 0) makes all records visible. With READ_COMMITTED (isolation_level = 1), non-transactional and COMMITTED transactional records are visible. To be more concrete, READ_COMMITTED returns all data from offsets smaller than the current LSO (last stable offset), and enables the inclusion of the list of aborted transactions in the result, which allows consumers to discard ABORTED transactional records /// - /// Supported API versions: 2-8 + /// Supported API versions: 2-9 pub fn with_isolation_level(mut self, value: i8) -> Self { self.isolation_level = value; self @@ -251,7 +251,7 @@ impl ListOffsetsRequest { /// /// Each topic in the request. /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub fn with_topics(mut self, value: Vec) -> Self { self.topics = value; self @@ -375,22 +375,22 @@ impl Default for ListOffsetsRequest { } impl Message for ListOffsetsRequest { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 8 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 9 }; const DEPRECATED_VERSIONS: Option = Some(VersionRange { min: 0, max: 0 }); } -/// Valid versions: 0-8 +/// Valid versions: 0-9 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct ListOffsetsTopic { /// The topic name. /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub name: super::TopicName, /// Each partition in the request. /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub partitions: Vec, /// Other tagged fields @@ -402,7 +402,7 @@ impl ListOffsetsTopic { /// /// The topic name. /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub fn with_name(mut self, value: super::TopicName) -> Self { self.name = value; self @@ -411,7 +411,7 @@ impl ListOffsetsTopic { /// /// Each partition in the request. /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub fn with_partitions(mut self, value: Vec) -> Self { self.partitions = value; self @@ -526,7 +526,7 @@ impl Default for ListOffsetsTopic { } impl Message for ListOffsetsTopic { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 8 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 9 }; const DEPRECATED_VERSIONS: Option = Some(VersionRange { min: 0, max: 0 }); } diff --git a/src/messages/list_offsets_response.rs b/src/messages/list_offsets_response.rs index a13ddc5..050f5a5 100644 --- a/src/messages/list_offsets_response.rs +++ b/src/messages/list_offsets_response.rs @@ -17,18 +17,18 @@ use crate::protocol::{ Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, }; -/// Valid versions: 0-8 +/// Valid versions: 0-9 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct ListOffsetsPartitionResponse { /// The partition index. /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub partition_index: i32, /// The partition error code, or 0 if there was no error. /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub error_code: i16, /// The result offsets. @@ -38,17 +38,17 @@ pub struct ListOffsetsPartitionResponse { /// The timestamp associated with the returned offset. /// - /// Supported API versions: 1-8 + /// Supported API versions: 1-9 pub timestamp: i64, /// The returned offset. /// - /// Supported API versions: 1-8 + /// Supported API versions: 1-9 pub offset: i64, /// /// - /// Supported API versions: 4-8 + /// Supported API versions: 4-9 pub leader_epoch: i32, /// Other tagged fields @@ -60,7 +60,7 @@ impl ListOffsetsPartitionResponse { /// /// The partition index. /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub fn with_partition_index(mut self, value: i32) -> Self { self.partition_index = value; self @@ -69,7 +69,7 @@ impl ListOffsetsPartitionResponse { /// /// The partition error code, or 0 if there was no error. /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub fn with_error_code(mut self, value: i16) -> Self { self.error_code = value; self @@ -87,7 +87,7 @@ impl ListOffsetsPartitionResponse { /// /// The timestamp associated with the returned offset. /// - /// Supported API versions: 1-8 + /// Supported API versions: 1-9 pub fn with_timestamp(mut self, value: i64) -> Self { self.timestamp = value; self @@ -96,7 +96,7 @@ impl ListOffsetsPartitionResponse { /// /// The returned offset. /// - /// Supported API versions: 1-8 + /// Supported API versions: 1-9 pub fn with_offset(mut self, value: i64) -> Self { self.offset = value; self @@ -105,7 +105,7 @@ impl ListOffsetsPartitionResponse { /// /// /// - /// Supported API versions: 4-8 + /// Supported API versions: 4-9 pub fn with_leader_epoch(mut self, value: i32) -> Self { self.leader_epoch = value; self @@ -279,22 +279,22 @@ impl Default for ListOffsetsPartitionResponse { } impl Message for ListOffsetsPartitionResponse { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 8 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 9 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0-8 +/// Valid versions: 0-9 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct ListOffsetsResponse { /// The duration in milliseconds for which the request was throttled due to a quota violation, or zero if the request did not violate any quota. /// - /// Supported API versions: 2-8 + /// Supported API versions: 2-9 pub throttle_time_ms: i32, /// Each topic in the response. /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub topics: Vec, /// Other tagged fields @@ -306,7 +306,7 @@ impl ListOffsetsResponse { /// /// The duration in milliseconds for which the request was throttled due to a quota violation, or zero if the request did not violate any quota. /// - /// Supported API versions: 2-8 + /// Supported API versions: 2-9 pub fn with_throttle_time_ms(mut self, value: i32) -> Self { self.throttle_time_ms = value; self @@ -315,7 +315,7 @@ impl ListOffsetsResponse { /// /// Each topic in the response. /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub fn with_topics(mut self, value: Vec) -> Self { self.topics = value; self @@ -426,22 +426,22 @@ impl Default for ListOffsetsResponse { } impl Message for ListOffsetsResponse { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 8 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 9 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0-8 +/// Valid versions: 0-9 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct ListOffsetsTopicResponse { /// The topic name /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub name: super::TopicName, /// Each partition in the response. /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub partitions: Vec, /// Other tagged fields @@ -453,7 +453,7 @@ impl ListOffsetsTopicResponse { /// /// The topic name /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub fn with_name(mut self, value: super::TopicName) -> Self { self.name = value; self @@ -462,7 +462,7 @@ impl ListOffsetsTopicResponse { /// /// Each partition in the response. /// - /// Supported API versions: 0-8 + /// Supported API versions: 0-9 pub fn with_partitions(mut self, value: Vec) -> Self { self.partitions = value; self @@ -577,7 +577,7 @@ impl Default for ListOffsetsTopicResponse { } impl Message for ListOffsetsTopicResponse { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 8 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 9 }; const DEPRECATED_VERSIONS: Option = None; } diff --git a/src/messages/remove_raft_voter_request.rs b/src/messages/remove_raft_voter_request.rs new file mode 100644 index 0000000..ec57206 --- /dev/null +++ b/src/messages/remove_raft_voter_request.rs @@ -0,0 +1,163 @@ +//! RemoveRaftVoterRequest +//! +//! See the schema for this message [here](https://github.com/apache/kafka/blob/trunk/clients/src/main/resources/common/message/RemoveRaftVoterRequest.json). +// WARNING: the items of this module are generated and should not be edited directly +#![allow(unused)] + +use std::borrow::Borrow; +use std::collections::BTreeMap; + +use anyhow::{bail, Result}; +use bytes::Bytes; +use uuid::Uuid; + +use crate::protocol::{ + buf::{ByteBuf, ByteBufMut}, + compute_unknown_tagged_fields_size, types, write_unknown_tagged_fields, Decodable, Decoder, + Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, +}; + +/// Valid versions: 0 +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct RemoveRaftVoterRequest { + /// + /// + /// Supported API versions: 0 + pub cluster_id: Option, + + /// The replica id of the voter getting removed from the topic partition + /// + /// Supported API versions: 0 + pub voter_id: i32, + + /// The directory id of the voter getting removed from the topic partition + /// + /// Supported API versions: 0 + pub voter_directory_id: Uuid, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, +} + +impl RemoveRaftVoterRequest { + /// Sets `cluster_id` to the passed value. + /// + /// + /// + /// Supported API versions: 0 + pub fn with_cluster_id(mut self, value: Option) -> Self { + self.cluster_id = value; + self + } + /// Sets `voter_id` to the passed value. + /// + /// The replica id of the voter getting removed from the topic partition + /// + /// Supported API versions: 0 + pub fn with_voter_id(mut self, value: i32) -> Self { + self.voter_id = value; + self + } + /// Sets `voter_directory_id` to the passed value. + /// + /// The directory id of the voter getting removed from the topic partition + /// + /// Supported API versions: 0 + pub fn with_voter_directory_id(mut self, value: Uuid) -> Self { + self.voter_directory_id = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } +} + +#[cfg(feature = "client")] +impl Encodable for RemoveRaftVoterRequest { + fn encode(&self, buf: &mut B, version: i16) -> Result<()> { + types::CompactString.encode(buf, &self.cluster_id)?; + types::Int32.encode(buf, &self.voter_id)?; + types::Uuid.encode(buf, &self.voter_directory_id)?; + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + Ok(()) + } + fn compute_size(&self, version: i16) -> Result { + let mut total_size = 0; + total_size += types::CompactString.compute_size(&self.cluster_id)?; + total_size += types::Int32.compute_size(&self.voter_id)?; + total_size += types::Uuid.compute_size(&self.voter_directory_id)?; + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + Ok(total_size) + } +} + +#[cfg(feature = "broker")] +impl Decodable for RemoveRaftVoterRequest { + fn decode(buf: &mut B, version: i16) -> Result { + let cluster_id = types::CompactString.decode(buf)?; + let voter_id = types::Int32.decode(buf)?; + let voter_directory_id = types::Uuid.decode(buf)?; + let mut unknown_tagged_fields = BTreeMap::new(); + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + Ok(Self { + cluster_id, + voter_id, + voter_directory_id, + unknown_tagged_fields, + }) + } +} + +impl Default for RemoveRaftVoterRequest { + fn default() -> Self { + Self { + cluster_id: Some(Default::default()), + voter_id: 0, + voter_directory_id: Uuid::nil(), + unknown_tagged_fields: BTreeMap::new(), + } + } +} + +impl Message for RemoveRaftVoterRequest { + const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const DEPRECATED_VERSIONS: Option = None; +} + +impl HeaderVersion for RemoveRaftVoterRequest { + fn header_version(version: i16) -> i16 { + 2 + } +} diff --git a/src/messages/remove_raft_voter_response.rs b/src/messages/remove_raft_voter_response.rs new file mode 100644 index 0000000..35b20ea --- /dev/null +++ b/src/messages/remove_raft_voter_response.rs @@ -0,0 +1,163 @@ +//! RemoveRaftVoterResponse +//! +//! See the schema for this message [here](https://github.com/apache/kafka/blob/trunk/clients/src/main/resources/common/message/RemoveRaftVoterResponse.json). +// WARNING: the items of this module are generated and should not be edited directly +#![allow(unused)] + +use std::borrow::Borrow; +use std::collections::BTreeMap; + +use anyhow::{bail, Result}; +use bytes::Bytes; +use uuid::Uuid; + +use crate::protocol::{ + buf::{ByteBuf, ByteBufMut}, + compute_unknown_tagged_fields_size, types, write_unknown_tagged_fields, Decodable, Decoder, + Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, +}; + +/// Valid versions: 0 +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct RemoveRaftVoterResponse { + /// The duration in milliseconds for which the request was throttled due to a quota violation, or zero if the request did not violate any quota. + /// + /// Supported API versions: 0 + pub throttle_time_ms: i32, + + /// The error code, or 0 if there was no error + /// + /// Supported API versions: 0 + pub error_code: i16, + + /// The error message, or null if there was no error. + /// + /// Supported API versions: 0 + pub error_message: Option, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, +} + +impl RemoveRaftVoterResponse { + /// Sets `throttle_time_ms` to the passed value. + /// + /// The duration in milliseconds for which the request was throttled due to a quota violation, or zero if the request did not violate any quota. + /// + /// Supported API versions: 0 + pub fn with_throttle_time_ms(mut self, value: i32) -> Self { + self.throttle_time_ms = value; + self + } + /// Sets `error_code` to the passed value. + /// + /// The error code, or 0 if there was no error + /// + /// Supported API versions: 0 + pub fn with_error_code(mut self, value: i16) -> Self { + self.error_code = value; + self + } + /// Sets `error_message` to the passed value. + /// + /// The error message, or null if there was no error. + /// + /// Supported API versions: 0 + pub fn with_error_message(mut self, value: Option) -> Self { + self.error_message = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } +} + +#[cfg(feature = "broker")] +impl Encodable for RemoveRaftVoterResponse { + fn encode(&self, buf: &mut B, version: i16) -> Result<()> { + types::Int32.encode(buf, &self.throttle_time_ms)?; + types::Int16.encode(buf, &self.error_code)?; + types::CompactString.encode(buf, &self.error_message)?; + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + Ok(()) + } + fn compute_size(&self, version: i16) -> Result { + let mut total_size = 0; + total_size += types::Int32.compute_size(&self.throttle_time_ms)?; + total_size += types::Int16.compute_size(&self.error_code)?; + total_size += types::CompactString.compute_size(&self.error_message)?; + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + Ok(total_size) + } +} + +#[cfg(feature = "client")] +impl Decodable for RemoveRaftVoterResponse { + fn decode(buf: &mut B, version: i16) -> Result { + let throttle_time_ms = types::Int32.decode(buf)?; + let error_code = types::Int16.decode(buf)?; + let error_message = types::CompactString.decode(buf)?; + let mut unknown_tagged_fields = BTreeMap::new(); + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + Ok(Self { + throttle_time_ms, + error_code, + error_message, + unknown_tagged_fields, + }) + } +} + +impl Default for RemoveRaftVoterResponse { + fn default() -> Self { + Self { + throttle_time_ms: 0, + error_code: 0, + error_message: Some(Default::default()), + unknown_tagged_fields: BTreeMap::new(), + } + } +} + +impl Message for RemoveRaftVoterResponse { + const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const DEPRECATED_VERSIONS: Option = None; +} + +impl HeaderVersion for RemoveRaftVoterResponse { + fn header_version(version: i16) -> i16 { + 1 + } +} diff --git a/src/messages/update_raft_voter_request.rs b/src/messages/update_raft_voter_request.rs new file mode 100644 index 0000000..7e41138 --- /dev/null +++ b/src/messages/update_raft_voter_request.rs @@ -0,0 +1,480 @@ +//! UpdateRaftVoterRequest +//! +//! See the schema for this message [here](https://github.com/apache/kafka/blob/trunk/clients/src/main/resources/common/message/UpdateRaftVoterRequest.json). +// WARNING: the items of this module are generated and should not be edited directly +#![allow(unused)] + +use std::borrow::Borrow; +use std::collections::BTreeMap; + +use anyhow::{bail, Result}; +use bytes::Bytes; +use uuid::Uuid; + +use crate::protocol::{ + buf::{ByteBuf, ByteBufMut}, + compute_unknown_tagged_fields_size, types, write_unknown_tagged_fields, Decodable, Decoder, + Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, +}; + +/// Valid versions: 0 +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct KRaftVersionFeature { + /// The minimum supported KRaft protocol version + /// + /// Supported API versions: 0 + pub min_supported_version: i16, + + /// The maximum supported KRaft protocol version + /// + /// Supported API versions: 0 + pub max_supported_version: i16, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, +} + +impl KRaftVersionFeature { + /// Sets `min_supported_version` to the passed value. + /// + /// The minimum supported KRaft protocol version + /// + /// Supported API versions: 0 + pub fn with_min_supported_version(mut self, value: i16) -> Self { + self.min_supported_version = value; + self + } + /// Sets `max_supported_version` to the passed value. + /// + /// The maximum supported KRaft protocol version + /// + /// Supported API versions: 0 + pub fn with_max_supported_version(mut self, value: i16) -> Self { + self.max_supported_version = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } +} + +#[cfg(feature = "client")] +impl Encodable for KRaftVersionFeature { + fn encode(&self, buf: &mut B, version: i16) -> Result<()> { + types::Int16.encode(buf, &self.min_supported_version)?; + types::Int16.encode(buf, &self.max_supported_version)?; + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + Ok(()) + } + fn compute_size(&self, version: i16) -> Result { + let mut total_size = 0; + total_size += types::Int16.compute_size(&self.min_supported_version)?; + total_size += types::Int16.compute_size(&self.max_supported_version)?; + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + Ok(total_size) + } +} + +#[cfg(feature = "broker")] +impl Decodable for KRaftVersionFeature { + fn decode(buf: &mut B, version: i16) -> Result { + let min_supported_version = types::Int16.decode(buf)?; + let max_supported_version = types::Int16.decode(buf)?; + let mut unknown_tagged_fields = BTreeMap::new(); + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + Ok(Self { + min_supported_version, + max_supported_version, + unknown_tagged_fields, + }) + } +} + +impl Default for KRaftVersionFeature { + fn default() -> Self { + Self { + min_supported_version: 0, + max_supported_version: 0, + unknown_tagged_fields: BTreeMap::new(), + } + } +} + +impl Message for KRaftVersionFeature { + const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const DEPRECATED_VERSIONS: Option = None; +} + +/// Valid versions: 0 +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct Listener { + /// The name of the endpoint + /// + /// Supported API versions: 0 + pub name: StrBytes, + + /// The hostname + /// + /// Supported API versions: 0 + pub host: StrBytes, + + /// The port + /// + /// Supported API versions: 0 + pub port: u16, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, +} + +impl Listener { + /// Sets `name` to the passed value. + /// + /// The name of the endpoint + /// + /// Supported API versions: 0 + pub fn with_name(mut self, value: StrBytes) -> Self { + self.name = value; + self + } + /// Sets `host` to the passed value. + /// + /// The hostname + /// + /// Supported API versions: 0 + pub fn with_host(mut self, value: StrBytes) -> Self { + self.host = value; + self + } + /// Sets `port` to the passed value. + /// + /// The port + /// + /// Supported API versions: 0 + pub fn with_port(mut self, value: u16) -> Self { + self.port = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } +} + +#[cfg(feature = "client")] +impl Encodable for Listener { + fn encode(&self, buf: &mut B, version: i16) -> Result<()> { + types::CompactString.encode(buf, &self.name)?; + types::CompactString.encode(buf, &self.host)?; + types::UInt16.encode(buf, &self.port)?; + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + Ok(()) + } + fn compute_size(&self, version: i16) -> Result { + let mut total_size = 0; + total_size += types::CompactString.compute_size(&self.name)?; + total_size += types::CompactString.compute_size(&self.host)?; + total_size += types::UInt16.compute_size(&self.port)?; + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + Ok(total_size) + } +} + +#[cfg(feature = "broker")] +impl Decodable for Listener { + fn decode(buf: &mut B, version: i16) -> Result { + let name = types::CompactString.decode(buf)?; + let host = types::CompactString.decode(buf)?; + let port = types::UInt16.decode(buf)?; + let mut unknown_tagged_fields = BTreeMap::new(); + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + Ok(Self { + name, + host, + port, + unknown_tagged_fields, + }) + } +} + +impl Default for Listener { + fn default() -> Self { + Self { + name: Default::default(), + host: Default::default(), + port: 0, + unknown_tagged_fields: BTreeMap::new(), + } + } +} + +impl Message for Listener { + const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const DEPRECATED_VERSIONS: Option = None; +} + +/// Valid versions: 0 +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct UpdateRaftVoterRequest { + /// + /// + /// Supported API versions: 0 + pub cluster_id: Option, + + /// The current leader epoch of the partition, -1 for unknown leader epoch + /// + /// Supported API versions: 0 + pub current_leader_epoch: i32, + + /// The replica id of the voter getting updated in the topic partition + /// + /// Supported API versions: 0 + pub voter_id: i32, + + /// The directory id of the voter getting updated in the topic partition + /// + /// Supported API versions: 0 + pub voter_directory_id: Uuid, + + /// The endpoint that can be used to communicate with the leader + /// + /// Supported API versions: 0 + pub listeners: Vec, + + /// The range of versions of the protocol that the replica supports + /// + /// Supported API versions: 0 + pub k_raft_version_feature: KRaftVersionFeature, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, +} + +impl UpdateRaftVoterRequest { + /// Sets `cluster_id` to the passed value. + /// + /// + /// + /// Supported API versions: 0 + pub fn with_cluster_id(mut self, value: Option) -> Self { + self.cluster_id = value; + self + } + /// Sets `current_leader_epoch` to the passed value. + /// + /// The current leader epoch of the partition, -1 for unknown leader epoch + /// + /// Supported API versions: 0 + pub fn with_current_leader_epoch(mut self, value: i32) -> Self { + self.current_leader_epoch = value; + self + } + /// Sets `voter_id` to the passed value. + /// + /// The replica id of the voter getting updated in the topic partition + /// + /// Supported API versions: 0 + pub fn with_voter_id(mut self, value: i32) -> Self { + self.voter_id = value; + self + } + /// Sets `voter_directory_id` to the passed value. + /// + /// The directory id of the voter getting updated in the topic partition + /// + /// Supported API versions: 0 + pub fn with_voter_directory_id(mut self, value: Uuid) -> Self { + self.voter_directory_id = value; + self + } + /// Sets `listeners` to the passed value. + /// + /// The endpoint that can be used to communicate with the leader + /// + /// Supported API versions: 0 + pub fn with_listeners(mut self, value: Vec) -> Self { + self.listeners = value; + self + } + /// Sets `k_raft_version_feature` to the passed value. + /// + /// The range of versions of the protocol that the replica supports + /// + /// Supported API versions: 0 + pub fn with_k_raft_version_feature(mut self, value: KRaftVersionFeature) -> Self { + self.k_raft_version_feature = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } +} + +#[cfg(feature = "client")] +impl Encodable for UpdateRaftVoterRequest { + fn encode(&self, buf: &mut B, version: i16) -> Result<()> { + types::CompactString.encode(buf, &self.cluster_id)?; + types::Int32.encode(buf, &self.current_leader_epoch)?; + types::Int32.encode(buf, &self.voter_id)?; + types::Uuid.encode(buf, &self.voter_directory_id)?; + types::CompactArray(types::Struct { version }).encode(buf, &self.listeners)?; + types::Struct { version }.encode(buf, &self.k_raft_version_feature)?; + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + Ok(()) + } + fn compute_size(&self, version: i16) -> Result { + let mut total_size = 0; + total_size += types::CompactString.compute_size(&self.cluster_id)?; + total_size += types::Int32.compute_size(&self.current_leader_epoch)?; + total_size += types::Int32.compute_size(&self.voter_id)?; + total_size += types::Uuid.compute_size(&self.voter_directory_id)?; + total_size += + types::CompactArray(types::Struct { version }).compute_size(&self.listeners)?; + total_size += types::Struct { version }.compute_size(&self.k_raft_version_feature)?; + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + Ok(total_size) + } +} + +#[cfg(feature = "broker")] +impl Decodable for UpdateRaftVoterRequest { + fn decode(buf: &mut B, version: i16) -> Result { + let cluster_id = types::CompactString.decode(buf)?; + let current_leader_epoch = types::Int32.decode(buf)?; + let voter_id = types::Int32.decode(buf)?; + let voter_directory_id = types::Uuid.decode(buf)?; + let listeners = types::CompactArray(types::Struct { version }).decode(buf)?; + let k_raft_version_feature = types::Struct { version }.decode(buf)?; + let mut unknown_tagged_fields = BTreeMap::new(); + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + Ok(Self { + cluster_id, + current_leader_epoch, + voter_id, + voter_directory_id, + listeners, + k_raft_version_feature, + unknown_tagged_fields, + }) + } +} + +impl Default for UpdateRaftVoterRequest { + fn default() -> Self { + Self { + cluster_id: Some(Default::default()), + current_leader_epoch: 0, + voter_id: 0, + voter_directory_id: Uuid::nil(), + listeners: Default::default(), + k_raft_version_feature: Default::default(), + unknown_tagged_fields: BTreeMap::new(), + } + } +} + +impl Message for UpdateRaftVoterRequest { + const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const DEPRECATED_VERSIONS: Option = None; +} + +impl HeaderVersion for UpdateRaftVoterRequest { + fn header_version(version: i16) -> i16 { + 2 + } +} diff --git a/src/messages/update_raft_voter_response.rs b/src/messages/update_raft_voter_response.rs new file mode 100644 index 0000000..7ac3086 --- /dev/null +++ b/src/messages/update_raft_voter_response.rs @@ -0,0 +1,356 @@ +//! UpdateRaftVoterResponse +//! +//! See the schema for this message [here](https://github.com/apache/kafka/blob/trunk/clients/src/main/resources/common/message/UpdateRaftVoterResponse.json). +// WARNING: the items of this module are generated and should not be edited directly +#![allow(unused)] + +use std::borrow::Borrow; +use std::collections::BTreeMap; + +use anyhow::{bail, Result}; +use bytes::Bytes; +use uuid::Uuid; + +use crate::protocol::{ + buf::{ByteBuf, ByteBufMut}, + compute_unknown_tagged_fields_size, types, write_unknown_tagged_fields, Decodable, Decoder, + Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, +}; + +/// Valid versions: 0 +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct CurrentLeader { + /// The replica id of the current leader or -1 if the leader is unknown + /// + /// Supported API versions: 0 + pub leader_id: super::BrokerId, + + /// The latest known leader epoch + /// + /// Supported API versions: 0 + pub leader_epoch: i32, + + /// The node's hostname + /// + /// Supported API versions: 0 + pub host: StrBytes, + + /// The node's port + /// + /// Supported API versions: 0 + pub port: i32, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, +} + +impl CurrentLeader { + /// Sets `leader_id` to the passed value. + /// + /// The replica id of the current leader or -1 if the leader is unknown + /// + /// Supported API versions: 0 + pub fn with_leader_id(mut self, value: super::BrokerId) -> Self { + self.leader_id = value; + self + } + /// Sets `leader_epoch` to the passed value. + /// + /// The latest known leader epoch + /// + /// Supported API versions: 0 + pub fn with_leader_epoch(mut self, value: i32) -> Self { + self.leader_epoch = value; + self + } + /// Sets `host` to the passed value. + /// + /// The node's hostname + /// + /// Supported API versions: 0 + pub fn with_host(mut self, value: StrBytes) -> Self { + self.host = value; + self + } + /// Sets `port` to the passed value. + /// + /// The node's port + /// + /// Supported API versions: 0 + pub fn with_port(mut self, value: i32) -> Self { + self.port = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } +} + +#[cfg(feature = "broker")] +impl Encodable for CurrentLeader { + fn encode(&self, buf: &mut B, version: i16) -> Result<()> { + types::Int32.encode(buf, &self.leader_id)?; + types::Int32.encode(buf, &self.leader_epoch)?; + types::CompactString.encode(buf, &self.host)?; + types::Int32.encode(buf, &self.port)?; + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + Ok(()) + } + fn compute_size(&self, version: i16) -> Result { + let mut total_size = 0; + total_size += types::Int32.compute_size(&self.leader_id)?; + total_size += types::Int32.compute_size(&self.leader_epoch)?; + total_size += types::CompactString.compute_size(&self.host)?; + total_size += types::Int32.compute_size(&self.port)?; + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + Ok(total_size) + } +} + +#[cfg(feature = "client")] +impl Decodable for CurrentLeader { + fn decode(buf: &mut B, version: i16) -> Result { + let leader_id = types::Int32.decode(buf)?; + let leader_epoch = types::Int32.decode(buf)?; + let host = types::CompactString.decode(buf)?; + let port = types::Int32.decode(buf)?; + let mut unknown_tagged_fields = BTreeMap::new(); + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + Ok(Self { + leader_id, + leader_epoch, + host, + port, + unknown_tagged_fields, + }) + } +} + +impl Default for CurrentLeader { + fn default() -> Self { + Self { + leader_id: (-1).into(), + leader_epoch: -1, + host: Default::default(), + port: 0, + unknown_tagged_fields: BTreeMap::new(), + } + } +} + +impl Message for CurrentLeader { + const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const DEPRECATED_VERSIONS: Option = None; +} + +/// Valid versions: 0 +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct UpdateRaftVoterResponse { + /// The duration in milliseconds for which the request was throttled due to a quota violation, or zero if the request did not violate any quota. + /// + /// Supported API versions: 0 + pub throttle_time_ms: i32, + + /// The error code, or 0 if there was no error + /// + /// Supported API versions: 0 + pub error_code: i16, + + /// + /// + /// Supported API versions: 0 + pub current_leader: CurrentLeader, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, +} + +impl UpdateRaftVoterResponse { + /// Sets `throttle_time_ms` to the passed value. + /// + /// The duration in milliseconds for which the request was throttled due to a quota violation, or zero if the request did not violate any quota. + /// + /// Supported API versions: 0 + pub fn with_throttle_time_ms(mut self, value: i32) -> Self { + self.throttle_time_ms = value; + self + } + /// Sets `error_code` to the passed value. + /// + /// The error code, or 0 if there was no error + /// + /// Supported API versions: 0 + pub fn with_error_code(mut self, value: i16) -> Self { + self.error_code = value; + self + } + /// Sets `current_leader` to the passed value. + /// + /// + /// + /// Supported API versions: 0 + pub fn with_current_leader(mut self, value: CurrentLeader) -> Self { + self.current_leader = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } +} + +#[cfg(feature = "broker")] +impl Encodable for UpdateRaftVoterResponse { + fn encode(&self, buf: &mut B, version: i16) -> Result<()> { + types::Int32.encode(buf, &self.throttle_time_ms)?; + types::Int16.encode(buf, &self.error_code)?; + let mut num_tagged_fields = self.unknown_tagged_fields.len(); + if &self.current_leader != &Default::default() { + num_tagged_fields += 1; + } + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + if &self.current_leader != &Default::default() { + let computed_size = types::Struct { version }.compute_size(&self.current_leader)?; + if computed_size > std::u32::MAX as usize { + bail!( + "Tagged field is too large to encode ({} bytes)", + computed_size + ); + } + types::UnsignedVarInt.encode(buf, 0)?; + types::UnsignedVarInt.encode(buf, computed_size as u32)?; + types::Struct { version }.encode(buf, &self.current_leader)?; + } + + write_unknown_tagged_fields(buf, 1.., &self.unknown_tagged_fields)?; + Ok(()) + } + fn compute_size(&self, version: i16) -> Result { + let mut total_size = 0; + total_size += types::Int32.compute_size(&self.throttle_time_ms)?; + total_size += types::Int16.compute_size(&self.error_code)?; + let mut num_tagged_fields = self.unknown_tagged_fields.len(); + if &self.current_leader != &Default::default() { + num_tagged_fields += 1; + } + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + if &self.current_leader != &Default::default() { + let computed_size = types::Struct { version }.compute_size(&self.current_leader)?; + if computed_size > std::u32::MAX as usize { + bail!( + "Tagged field is too large to encode ({} bytes)", + computed_size + ); + } + total_size += types::UnsignedVarInt.compute_size(0)?; + total_size += types::UnsignedVarInt.compute_size(computed_size as u32)?; + total_size += computed_size; + } + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + Ok(total_size) + } +} + +#[cfg(feature = "client")] +impl Decodable for UpdateRaftVoterResponse { + fn decode(buf: &mut B, version: i16) -> Result { + let throttle_time_ms = types::Int32.decode(buf)?; + let error_code = types::Int16.decode(buf)?; + let mut current_leader = Default::default(); + let mut unknown_tagged_fields = BTreeMap::new(); + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + match tag { + 0 => { + current_leader = types::Struct { version }.decode(buf)?; + } + _ => { + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } + } + Ok(Self { + throttle_time_ms, + error_code, + current_leader, + unknown_tagged_fields, + }) + } +} + +impl Default for UpdateRaftVoterResponse { + fn default() -> Self { + Self { + throttle_time_ms: 0, + error_code: 0, + current_leader: Default::default(), + unknown_tagged_fields: BTreeMap::new(), + } + } +} + +impl Message for UpdateRaftVoterResponse { + const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const DEPRECATED_VERSIONS: Option = None; +} + +impl HeaderVersion for UpdateRaftVoterResponse { + fn header_version(version: i16) -> i16 { + 1 + } +} diff --git a/src/messages/vote_request.rs b/src/messages/vote_request.rs index 051dcba..458b113 100644 --- a/src/messages/vote_request.rs +++ b/src/messages/vote_request.rs @@ -17,33 +17,43 @@ use crate::protocol::{ Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, }; -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct PartitionData { /// The partition index. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub partition_index: i32, /// The bumped epoch of the candidate sending the request /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub candidate_epoch: i32, - /// The ID of the voter sending the request + /// The replica id of the voter sending the request /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub candidate_id: super::BrokerId, + /// The directory id of the voter sending the request + /// + /// Supported API versions: 1 + pub candidate_directory_id: Uuid, + + /// The ID of the voter sending the request + /// + /// Supported API versions: 1 + pub voter_directory_id: Uuid, + /// The epoch of the last record written to the metadata log /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub last_offset_epoch: i32, /// The offset of the last record written to the metadata log /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub last_offset: i64, /// Other tagged fields @@ -55,7 +65,7 @@ impl PartitionData { /// /// The partition index. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_partition_index(mut self, value: i32) -> Self { self.partition_index = value; self @@ -64,25 +74,43 @@ impl PartitionData { /// /// The bumped epoch of the candidate sending the request /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_candidate_epoch(mut self, value: i32) -> Self { self.candidate_epoch = value; self } /// Sets `candidate_id` to the passed value. /// - /// The ID of the voter sending the request + /// The replica id of the voter sending the request /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_candidate_id(mut self, value: super::BrokerId) -> Self { self.candidate_id = value; self } + /// Sets `candidate_directory_id` to the passed value. + /// + /// The directory id of the voter sending the request + /// + /// Supported API versions: 1 + pub fn with_candidate_directory_id(mut self, value: Uuid) -> Self { + self.candidate_directory_id = value; + self + } + /// Sets `voter_directory_id` to the passed value. + /// + /// The ID of the voter sending the request + /// + /// Supported API versions: 1 + pub fn with_voter_directory_id(mut self, value: Uuid) -> Self { + self.voter_directory_id = value; + self + } /// Sets `last_offset_epoch` to the passed value. /// /// The epoch of the last record written to the metadata log /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_last_offset_epoch(mut self, value: i32) -> Self { self.last_offset_epoch = value; self @@ -91,7 +119,7 @@ impl PartitionData { /// /// The offset of the last record written to the metadata log /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_last_offset(mut self, value: i64) -> Self { self.last_offset = value; self @@ -114,6 +142,12 @@ impl Encodable for PartitionData { types::Int32.encode(buf, &self.partition_index)?; types::Int32.encode(buf, &self.candidate_epoch)?; types::Int32.encode(buf, &self.candidate_id)?; + if version >= 1 { + types::Uuid.encode(buf, &self.candidate_directory_id)?; + } + if version >= 1 { + types::Uuid.encode(buf, &self.voter_directory_id)?; + } types::Int32.encode(buf, &self.last_offset_epoch)?; types::Int64.encode(buf, &self.last_offset)?; let num_tagged_fields = self.unknown_tagged_fields.len(); @@ -133,6 +167,12 @@ impl Encodable for PartitionData { total_size += types::Int32.compute_size(&self.partition_index)?; total_size += types::Int32.compute_size(&self.candidate_epoch)?; total_size += types::Int32.compute_size(&self.candidate_id)?; + if version >= 1 { + total_size += types::Uuid.compute_size(&self.candidate_directory_id)?; + } + if version >= 1 { + total_size += types::Uuid.compute_size(&self.voter_directory_id)?; + } total_size += types::Int32.compute_size(&self.last_offset_epoch)?; total_size += types::Int64.compute_size(&self.last_offset)?; let num_tagged_fields = self.unknown_tagged_fields.len(); @@ -155,6 +195,16 @@ impl Decodable for PartitionData { let partition_index = types::Int32.decode(buf)?; let candidate_epoch = types::Int32.decode(buf)?; let candidate_id = types::Int32.decode(buf)?; + let candidate_directory_id = if version >= 1 { + types::Uuid.decode(buf)? + } else { + Uuid::nil() + }; + let voter_directory_id = if version >= 1 { + types::Uuid.decode(buf)? + } else { + Uuid::nil() + }; let last_offset_epoch = types::Int32.decode(buf)?; let last_offset = types::Int64.decode(buf)?; let mut unknown_tagged_fields = BTreeMap::new(); @@ -169,6 +219,8 @@ impl Decodable for PartitionData { partition_index, candidate_epoch, candidate_id, + candidate_directory_id, + voter_directory_id, last_offset_epoch, last_offset, unknown_tagged_fields, @@ -182,6 +234,8 @@ impl Default for PartitionData { partition_index: 0, candidate_epoch: 0, candidate_id: (0).into(), + candidate_directory_id: Uuid::nil(), + voter_directory_id: Uuid::nil(), last_offset_epoch: 0, last_offset: 0, unknown_tagged_fields: BTreeMap::new(), @@ -190,22 +244,22 @@ impl Default for PartitionData { } impl Message for PartitionData { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct TopicData { /// The topic name. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub topic_name: super::TopicName, /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub partitions: Vec, /// Other tagged fields @@ -217,7 +271,7 @@ impl TopicData { /// /// The topic name. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_topic_name(mut self, value: super::TopicName) -> Self { self.topic_name = value; self @@ -226,7 +280,7 @@ impl TopicData { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_partitions(mut self, value: Vec) -> Self { self.partitions = value; self @@ -311,22 +365,27 @@ impl Default for TopicData { } impl Message for TopicData { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct VoteRequest { /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub cluster_id: Option, + /// The replica id of the voter receiving the request + /// + /// Supported API versions: 1 + pub voter_id: super::BrokerId, + /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub topics: Vec, /// Other tagged fields @@ -338,16 +397,25 @@ impl VoteRequest { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_cluster_id(mut self, value: Option) -> Self { self.cluster_id = value; self } + /// Sets `voter_id` to the passed value. + /// + /// The replica id of the voter receiving the request + /// + /// Supported API versions: 1 + pub fn with_voter_id(mut self, value: super::BrokerId) -> Self { + self.voter_id = value; + self + } /// Sets `topics` to the passed value. /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_topics(mut self, value: Vec) -> Self { self.topics = value; self @@ -368,6 +436,9 @@ impl VoteRequest { impl Encodable for VoteRequest { fn encode(&self, buf: &mut B, version: i16) -> Result<()> { types::CompactString.encode(buf, &self.cluster_id)?; + if version >= 1 { + types::Int32.encode(buf, &self.voter_id)?; + } types::CompactArray(types::Struct { version }).encode(buf, &self.topics)?; let num_tagged_fields = self.unknown_tagged_fields.len(); if num_tagged_fields > std::u32::MAX as usize { @@ -384,6 +455,9 @@ impl Encodable for VoteRequest { fn compute_size(&self, version: i16) -> Result { let mut total_size = 0; total_size += types::CompactString.compute_size(&self.cluster_id)?; + if version >= 1 { + total_size += types::Int32.compute_size(&self.voter_id)?; + } total_size += types::CompactArray(types::Struct { version }).compute_size(&self.topics)?; let num_tagged_fields = self.unknown_tagged_fields.len(); if num_tagged_fields > std::u32::MAX as usize { @@ -403,6 +477,11 @@ impl Encodable for VoteRequest { impl Decodable for VoteRequest { fn decode(buf: &mut B, version: i16) -> Result { let cluster_id = types::CompactString.decode(buf)?; + let voter_id = if version >= 1 { + types::Int32.decode(buf)? + } else { + (-1).into() + }; let topics = types::CompactArray(types::Struct { version }).decode(buf)?; let mut unknown_tagged_fields = BTreeMap::new(); let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; @@ -414,6 +493,7 @@ impl Decodable for VoteRequest { } Ok(Self { cluster_id, + voter_id, topics, unknown_tagged_fields, }) @@ -424,6 +504,7 @@ impl Default for VoteRequest { fn default() -> Self { Self { cluster_id: None, + voter_id: (-1).into(), topics: Default::default(), unknown_tagged_fields: BTreeMap::new(), } @@ -431,7 +512,7 @@ impl Default for VoteRequest { } impl Message for VoteRequest { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } diff --git a/src/messages/vote_response.rs b/src/messages/vote_response.rs index 557ae3a..ecf3672 100644 --- a/src/messages/vote_response.rs +++ b/src/messages/vote_response.rs @@ -17,33 +17,220 @@ use crate::protocol::{ Encodable, Encoder, HeaderVersion, Message, StrBytes, VersionRange, }; -/// Valid versions: 0 +/// Valid versions: 0-1 +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq)] +pub struct NodeEndpoint { + /// The ID of the associated node + /// + /// Supported API versions: 1 + pub node_id: super::BrokerId, + + /// The node's hostname + /// + /// Supported API versions: 1 + pub host: StrBytes, + + /// The node's port + /// + /// Supported API versions: 1 + pub port: u16, + + /// Other tagged fields + pub unknown_tagged_fields: BTreeMap, +} + +impl NodeEndpoint { + /// Sets `node_id` to the passed value. + /// + /// The ID of the associated node + /// + /// Supported API versions: 1 + pub fn with_node_id(mut self, value: super::BrokerId) -> Self { + self.node_id = value; + self + } + /// Sets `host` to the passed value. + /// + /// The node's hostname + /// + /// Supported API versions: 1 + pub fn with_host(mut self, value: StrBytes) -> Self { + self.host = value; + self + } + /// Sets `port` to the passed value. + /// + /// The node's port + /// + /// Supported API versions: 1 + pub fn with_port(mut self, value: u16) -> Self { + self.port = value; + self + } + /// Sets unknown_tagged_fields to the passed value. + pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { + self.unknown_tagged_fields = value; + self + } + /// Inserts an entry into unknown_tagged_fields. + pub fn with_unknown_tagged_field(mut self, key: i32, value: Bytes) -> Self { + self.unknown_tagged_fields.insert(key, value); + self + } +} + +#[cfg(feature = "broker")] +impl Encodable for NodeEndpoint { + fn encode(&self, buf: &mut B, version: i16) -> Result<()> { + if version >= 1 { + types::Int32.encode(buf, &self.node_id)?; + } else { + if self.node_id != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + types::CompactString.encode(buf, &self.host)?; + } else { + if !self.host.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + types::UInt16.encode(buf, &self.port)?; + } else { + if self.port != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; + + write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + Ok(()) + } + fn compute_size(&self, version: i16) -> Result { + let mut total_size = 0; + if version >= 1 { + total_size += types::Int32.compute_size(&self.node_id)?; + } else { + if self.node_id != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + total_size += types::CompactString.compute_size(&self.host)?; + } else { + if !self.host.is_empty() { + bail!("A field is set that is not available on the selected protocol version"); + } + } + if version >= 1 { + total_size += types::UInt16.compute_size(&self.port)?; + } else { + if self.port != 0 { + bail!("A field is set that is not available on the selected protocol version"); + } + } + let num_tagged_fields = self.unknown_tagged_fields.len(); + if num_tagged_fields > std::u32::MAX as usize { + bail!( + "Too many tagged fields to encode ({} fields)", + num_tagged_fields + ); + } + total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; + + total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; + Ok(total_size) + } +} + +#[cfg(feature = "client")] +impl Decodable for NodeEndpoint { + fn decode(buf: &mut B, version: i16) -> Result { + let node_id = if version >= 1 { + types::Int32.decode(buf)? + } else { + (0).into() + }; + let host = if version >= 1 { + types::CompactString.decode(buf)? + } else { + Default::default() + }; + let port = if version >= 1 { + types::UInt16.decode(buf)? + } else { + 0 + }; + let mut unknown_tagged_fields = BTreeMap::new(); + let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; + for _ in 0..num_tagged_fields { + let tag: u32 = types::UnsignedVarInt.decode(buf)?; + let size: u32 = types::UnsignedVarInt.decode(buf)?; + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + Ok(Self { + node_id, + host, + port, + unknown_tagged_fields, + }) + } +} + +impl Default for NodeEndpoint { + fn default() -> Self { + Self { + node_id: (0).into(), + host: Default::default(), + port: 0, + unknown_tagged_fields: BTreeMap::new(), + } + } +} + +impl Message for NodeEndpoint { + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; + const DEPRECATED_VERSIONS: Option = None; +} + +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct PartitionData { /// The partition index. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub partition_index: i32, /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub error_code: i16, /// The ID of the current leader or -1 if the leader is unknown. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub leader_id: super::BrokerId, /// The latest known leader epoch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub leader_epoch: i32, /// True if the vote was granted and false otherwise /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub vote_granted: bool, /// Other tagged fields @@ -55,7 +242,7 @@ impl PartitionData { /// /// The partition index. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_partition_index(mut self, value: i32) -> Self { self.partition_index = value; self @@ -64,7 +251,7 @@ impl PartitionData { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_error_code(mut self, value: i16) -> Self { self.error_code = value; self @@ -73,7 +260,7 @@ impl PartitionData { /// /// The ID of the current leader or -1 if the leader is unknown. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_leader_id(mut self, value: super::BrokerId) -> Self { self.leader_id = value; self @@ -82,7 +269,7 @@ impl PartitionData { /// /// The latest known leader epoch /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_leader_epoch(mut self, value: i32) -> Self { self.leader_epoch = value; self @@ -91,7 +278,7 @@ impl PartitionData { /// /// True if the vote was granted and false otherwise /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_vote_granted(mut self, value: bool) -> Self { self.vote_granted = value; self @@ -190,22 +377,22 @@ impl Default for PartitionData { } impl Message for PartitionData { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct TopicData { /// The topic name. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub topic_name: super::TopicName, /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub partitions: Vec, /// Other tagged fields @@ -217,7 +404,7 @@ impl TopicData { /// /// The topic name. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_topic_name(mut self, value: super::TopicName) -> Self { self.topic_name = value; self @@ -226,7 +413,7 @@ impl TopicData { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_partitions(mut self, value: Vec) -> Self { self.partitions = value; self @@ -311,24 +498,29 @@ impl Default for TopicData { } impl Message for TopicData { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; } -/// Valid versions: 0 +/// Valid versions: 0-1 #[non_exhaustive] #[derive(Debug, Clone, PartialEq)] pub struct VoteResponse { /// The top level error code. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub error_code: i16, /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub topics: Vec, + /// Endpoints for all current-leaders enumerated in PartitionData + /// + /// Supported API versions: 1 + pub node_endpoints: Vec, + /// Other tagged fields pub unknown_tagged_fields: BTreeMap, } @@ -338,7 +530,7 @@ impl VoteResponse { /// /// The top level error code. /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_error_code(mut self, value: i16) -> Self { self.error_code = value; self @@ -347,11 +539,20 @@ impl VoteResponse { /// /// /// - /// Supported API versions: 0 + /// Supported API versions: 0-1 pub fn with_topics(mut self, value: Vec) -> Self { self.topics = value; self } + /// Sets `node_endpoints` to the passed value. + /// + /// Endpoints for all current-leaders enumerated in PartitionData + /// + /// Supported API versions: 1 + pub fn with_node_endpoints(mut self, value: Vec) -> Self { + self.node_endpoints = value; + self + } /// Sets unknown_tagged_fields to the passed value. pub fn with_unknown_tagged_fields(mut self, value: BTreeMap) -> Self { self.unknown_tagged_fields = value; @@ -369,7 +570,12 @@ impl Encodable for VoteResponse { fn encode(&self, buf: &mut B, version: i16) -> Result<()> { types::Int16.encode(buf, &self.error_code)?; types::CompactArray(types::Struct { version }).encode(buf, &self.topics)?; - let num_tagged_fields = self.unknown_tagged_fields.len(); + let mut num_tagged_fields = self.unknown_tagged_fields.len(); + if version >= 1 { + if !self.node_endpoints.is_empty() { + num_tagged_fields += 1; + } + } if num_tagged_fields > std::u32::MAX as usize { bail!( "Too many tagged fields to encode ({} fields)", @@ -377,15 +583,34 @@ impl Encodable for VoteResponse { ); } types::UnsignedVarInt.encode(buf, num_tagged_fields as u32)?; - - write_unknown_tagged_fields(buf, 0.., &self.unknown_tagged_fields)?; + if version >= 1 { + if !self.node_endpoints.is_empty() { + let computed_size = types::CompactArray(types::Struct { version }) + .compute_size(&self.node_endpoints)?; + if computed_size > std::u32::MAX as usize { + bail!( + "Tagged field is too large to encode ({} bytes)", + computed_size + ); + } + types::UnsignedVarInt.encode(buf, 0)?; + types::UnsignedVarInt.encode(buf, computed_size as u32)?; + types::CompactArray(types::Struct { version }).encode(buf, &self.node_endpoints)?; + } + } + write_unknown_tagged_fields(buf, 1.., &self.unknown_tagged_fields)?; Ok(()) } fn compute_size(&self, version: i16) -> Result { let mut total_size = 0; total_size += types::Int16.compute_size(&self.error_code)?; total_size += types::CompactArray(types::Struct { version }).compute_size(&self.topics)?; - let num_tagged_fields = self.unknown_tagged_fields.len(); + let mut num_tagged_fields = self.unknown_tagged_fields.len(); + if version >= 1 { + if !self.node_endpoints.is_empty() { + num_tagged_fields += 1; + } + } if num_tagged_fields > std::u32::MAX as usize { bail!( "Too many tagged fields to encode ({} fields)", @@ -393,7 +618,21 @@ impl Encodable for VoteResponse { ); } total_size += types::UnsignedVarInt.compute_size(num_tagged_fields as u32)?; - + if version >= 1 { + if !self.node_endpoints.is_empty() { + let computed_size = types::CompactArray(types::Struct { version }) + .compute_size(&self.node_endpoints)?; + if computed_size > std::u32::MAX as usize { + bail!( + "Tagged field is too large to encode ({} bytes)", + computed_size + ); + } + total_size += types::UnsignedVarInt.compute_size(0)?; + total_size += types::UnsignedVarInt.compute_size(computed_size as u32)?; + total_size += computed_size; + } + } total_size += compute_unknown_tagged_fields_size(&self.unknown_tagged_fields)?; Ok(total_size) } @@ -404,17 +643,31 @@ impl Decodable for VoteResponse { fn decode(buf: &mut B, version: i16) -> Result { let error_code = types::Int16.decode(buf)?; let topics = types::CompactArray(types::Struct { version }).decode(buf)?; + let mut node_endpoints = Default::default(); let mut unknown_tagged_fields = BTreeMap::new(); let num_tagged_fields = types::UnsignedVarInt.decode(buf)?; for _ in 0..num_tagged_fields { let tag: u32 = types::UnsignedVarInt.decode(buf)?; let size: u32 = types::UnsignedVarInt.decode(buf)?; - let unknown_value = buf.try_get_bytes(size as usize)?; - unknown_tagged_fields.insert(tag as i32, unknown_value); + match tag { + 0 => { + if version >= 1 { + node_endpoints = + types::CompactArray(types::Struct { version }).decode(buf)?; + } else { + bail!("Tag {} is not valid for version {}", tag, version); + } + } + _ => { + let unknown_value = buf.try_get_bytes(size as usize)?; + unknown_tagged_fields.insert(tag as i32, unknown_value); + } + } } Ok(Self { error_code, topics, + node_endpoints, unknown_tagged_fields, }) } @@ -425,13 +678,14 @@ impl Default for VoteResponse { Self { error_code: 0, topics: Default::default(), + node_endpoints: Default::default(), unknown_tagged_fields: BTreeMap::new(), } } } impl Message for VoteResponse { - const VERSIONS: VersionRange = VersionRange { min: 0, max: 0 }; + const VERSIONS: VersionRange = VersionRange { min: 0, max: 1 }; const DEPRECATED_VERSIONS: Option = None; }