use std::sync::atomic::{AtomicUsize, Ordering};
use crate::common::{parse_server_address, GRPC_PORT_OFFSET};
use crate::error::{BatataError, Result};
#[derive(Clone, Debug)]
pub struct ServerAddress {
pub host: String,
pub port: u16,
pub grpc_port: u16,
pub tls_enabled: bool,
}
impl ServerAddress {
pub fn new(host: &str, port: u16) -> Self {
Self {
host: host.to_string(),
port,
grpc_port: port + GRPC_PORT_OFFSET,
tls_enabled: false,
}
}
pub fn with_tls(mut self, enabled: bool) -> Self {
self.tls_enabled = enabled;
self
}
pub fn host(&self) -> &str {
&self.host
}
pub fn port(&self) -> u16 {
self.port
}
pub fn address(&self) -> String {
format!("{}:{}", self.host, self.port)
}
pub fn grpc_endpoint(&self) -> String {
let scheme = if self.tls_enabled { "https" } else { "http" };
format!("{}://{}:{}", scheme, self.host, self.grpc_port)
}
pub fn http_endpoint(&self) -> String {
let scheme = if self.tls_enabled { "https" } else { "http" };
format!("{}://{}:{}", scheme, self.host, self.port)
}
}
pub struct ServerListManager {
servers: Vec<ServerAddress>,
current_index: AtomicUsize,
}
impl ServerListManager {
pub fn new(addresses: Vec<String>) -> Result<Self> {
if addresses.is_empty() {
return Err(BatataError::InvalidParameter(
"Server addresses cannot be empty".to_string(),
));
}
let servers: Vec<ServerAddress> = addresses
.iter()
.map(|addr| {
let (host, port) = parse_server_address(addr);
ServerAddress::new(&host, port)
})
.collect();
Ok(Self {
servers,
current_index: AtomicUsize::new(0),
})
}
pub fn next_server(&self) -> &ServerAddress {
let index = self.current_index.fetch_add(1, Ordering::Relaxed) % self.servers.len();
&self.servers[index]
}
pub fn current_server(&self) -> &ServerAddress {
let index = self.current_index.load(Ordering::Relaxed) % self.servers.len();
&self.servers[index]
}
pub fn all_servers(&self) -> &[ServerAddress] {
&self.servers
}
pub fn server_count(&self) -> usize {
self.servers.len()
}
pub fn fail_current(&self) -> &ServerAddress {
self.next_server()
}
}
impl Clone for ServerListManager {
fn clone(&self) -> Self {
Self {
servers: self.servers.clone(),
current_index: AtomicUsize::new(self.current_index.load(Ordering::Relaxed)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_server_address() {
let addr = ServerAddress::new("localhost", 8848);
assert_eq!(addr.grpc_port, 9848);
assert_eq!(addr.grpc_endpoint(), "http://localhost:9848");
assert_eq!(addr.http_endpoint(), "http://localhost:8848");
}
#[test]
fn test_server_list_manager() {
let manager = ServerListManager::new(vec![
"localhost:8848".to_string(),
"localhost:8849".to_string(),
])
.unwrap();
assert_eq!(manager.server_count(), 2);
let first = manager.next_server().host.clone();
let second = manager.next_server().host.clone();
let third = manager.next_server().host.clone();
assert_eq!(first, "localhost");
assert_eq!(second, "localhost");
assert_eq!(third, "localhost");
}
}