pub mod admin;
mod cluster;
pub mod connection; mod consumer;
mod error;
mod producer;
mod sasl;
pub mod transport; mod wire;
pub use error::{KafkaError, KafkaErrorCode, Result};
pub use kafka_client_protocol as protocol;
pub use sasl::{SaslCredentials, SaslMechanismType};
pub use transport::{SecurityProtocol, TlsConfig};
pub use producer::{
Header, PartitionRouter, PartitionRouting, Producer, ProducerConfig, ProducerRecord,
RecordMetadata,
};
pub use consumer::{
AutoOffsetReset, Consumer, ConsumerConfig, ConsumerRecord, ConsumerStream, GroupHandle,
OffsetHandle, PartitionAssignmentStrategy,
};
pub use cluster::MetadataCache;
pub const NAME: &str = env!("CARGO_PKG_NAME");
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
use std::sync::Arc;
use std::time::Duration;
use crate::cluster::ClusterClient;
pub struct Client {
cluster: Arc<ClusterClient>,
}
impl Client {
pub fn builder(bootstrap_servers: Vec<String>) -> ClientBuilder {
ClientBuilder::new(bootstrap_servers)
}
pub async fn producer_default(&self) -> Producer {
Producer::new(self.cluster.clone(), ProducerConfig::default()).await
}
pub async fn producer(&self, config: ProducerConfig) -> Producer {
Producer::new(self.cluster.clone(), config).await
}
pub fn consumer_default(&self) -> Consumer {
Consumer::new(self.cluster.clone(), ConsumerConfig::default())
}
pub fn consumer(&self, config: ConsumerConfig) -> Consumer {
Consumer::new(self.cluster.clone(), config)
}
pub fn admin(&self) -> admin::AdminClient {
admin::AdminClient::new(self.cluster.clone())
}
pub fn metadata(&self) -> &MetadataCache {
self.cluster.metadata()
}
pub async fn refresh_metadata(&self) -> Result<()> {
self.cluster.refresh_metadata().await
}
pub async fn send_to_any_broker<Req, Resp>(&self, request: &Req) -> Result<Resp>
where
Req: kafka_client_protocol::Request,
Resp: kafka_client_protocol::Response,
{
self.cluster.send_to_any_broker(request).await
}
pub async fn close(&self) -> Result<()> {
self.cluster.close().await
}
}
pub struct ClientBuilder {
bootstrap_servers: Vec<String>,
security_protocol: crate::transport::SecurityProtocol,
client_id: String,
sasl_credentials: Option<crate::sasl::SaslCredentials>,
metadata_ttl: Duration,
}
impl ClientBuilder {
pub fn new(bootstrap_servers: Vec<String>) -> Self {
Self {
bootstrap_servers,
security_protocol: crate::transport::SecurityProtocol::Plaintext,
client_id: NAME.to_string(),
sasl_credentials: None,
metadata_ttl: Duration::from_secs(300),
}
}
pub fn with_plaintext(mut self) -> Self {
self.security_protocol = crate::transport::SecurityProtocol::Plaintext;
self
}
pub fn with_tls(mut self, domain: impl Into<String>) -> Self {
self.security_protocol =
crate::transport::SecurityProtocol::Ssl(crate::transport::TlsConfig {
domain: domain.into(),
..Default::default()
});
self
}
pub fn with_tls_config(mut self, tls_config: crate::transport::TlsConfig) -> Self {
self.security_protocol = crate::transport::SecurityProtocol::Ssl(tls_config);
self
}
pub fn with_sasl(
mut self,
mechanism: crate::sasl::SaslMechanismType,
username: impl Into<String>,
password: impl Into<String>,
) -> Self {
self.security_protocol = crate::transport::SecurityProtocol::SaslPlaintext;
self.sasl_credentials = Some(crate::sasl::SaslCredentials::new(
mechanism, username, password,
));
self
}
pub fn with_sasl_tls(
mut self,
tls_config: crate::transport::TlsConfig,
mechanism: crate::sasl::SaslMechanismType,
username: impl Into<String>,
password: impl Into<String>,
) -> Self {
self.security_protocol = crate::transport::SecurityProtocol::SaslSsl(tls_config);
self.sasl_credentials = Some(crate::sasl::SaslCredentials::new(
mechanism, username, password,
));
self
}
pub fn with_sasl_plaintext(
self,
username: impl Into<String>,
password: impl Into<String>,
) -> Self {
self.with_sasl(crate::sasl::SaslMechanismType::Plain, username, password)
}
pub fn with_sasl_ssl(
self,
domain: impl Into<String>,
username: impl Into<String>,
password: impl Into<String>,
) -> Self {
let tls_config = crate::transport::TlsConfig {
domain: domain.into(),
..Default::default()
};
self.with_sasl_tls(
tls_config,
crate::sasl::SaslMechanismType::Plain,
username,
password,
)
}
pub fn with_client_id(mut self, client_id: impl Into<String>) -> Self {
self.client_id = client_id.into();
self
}
pub fn with_metadata_ttl(mut self, ttl: Duration) -> Self {
self.metadata_ttl = ttl;
self
}
pub async fn build(self) -> Result<Client> {
let mut resolved = Vec::with_capacity(self.bootstrap_servers.len());
for server in &self.bootstrap_servers {
match tokio::net::lookup_host(server).await {
Ok(mut addrs) => {
if let Some(addr) = addrs.next() {
resolved.push(addr);
} else {
return Err(KafkaError::Io(format!(
"Failed to resolve bootstrap server: {}",
server
)));
}
}
Err(e) => {
return Err(KafkaError::Io(format!(
"Failed to resolve bootstrap server '{}': {}",
server, e
)));
}
}
}
let config = crate::cluster::ClusterConfig {
bootstrap_servers: resolved,
security_protocol: self.security_protocol,
client_id: self.client_id,
metadata_ttl: self.metadata_ttl,
sasl: self.sasl_credentials,
};
let cluster = ClusterClient::connect(config).await?;
Ok(Client {
cluster: Arc::new(cluster),
})
}
}
pub fn builder(bootstrap_servers: Vec<String>) -> ClientBuilder {
ClientBuilder::new(bootstrap_servers)
}