use std::collections::BTreeMap;
use std::path::PathBuf;
use std::time::Duration;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MemberAddr {
pub raft_addr: String,
pub service_endpoint: String,
pub admin_endpoint: String,
}
#[derive(Debug, Clone)]
pub struct RaftTuning {
pub heartbeat_ms: u64,
pub election_min_ms: u64,
pub election_max_ms: u64,
}
impl Default for RaftTuning {
fn default() -> Self {
Self {
heartbeat_ms: 250,
election_min_ms: 1_000,
election_max_ms: 2_000,
}
}
}
#[derive(Debug, Clone)]
pub struct FileConfig {
pub state_dir: PathBuf,
}
#[derive(Debug, Clone)]
pub struct PeerTlsConfig {
pub cert: PathBuf,
pub key: PathBuf,
pub ca: PathBuf,
}
#[derive(Debug, Clone)]
pub struct OpenraftConfig {
pub id: u64,
pub raft_addr: std::net::SocketAddr,
pub raft_dir: PathBuf,
pub bootstrap: bool,
pub initial_membership: Option<BTreeMap<u64, MemberAddr>>,
pub tuning: RaftTuning,
pub peer_tls: Option<PeerTlsConfig>,
pub admin_listen: Option<std::net::SocketAddr>,
}
#[derive(Debug, Clone)]
pub struct PaxosConfig {
pub node_id: u64,
pub peer_listen: std::net::SocketAddr,
pub peers: BTreeMap<u64, String>,
pub tso_peers: BTreeMap<u64, String>,
pub data_dir: PathBuf,
pub tick_interval: Duration,
pub peer_tls: Option<PeerTlsConfig>,
}
pub enum DriverConfig {
#[cfg(feature = "file")]
File(FileConfig),
#[cfg(feature = "openraft")]
Openraft(OpenraftConfig),
#[cfg(feature = "paxos")]
Paxos(PaxosConfig),
}
pub fn parse_peer_map(input: &str) -> Result<BTreeMap<u64, String>, String> {
let mut out = BTreeMap::new();
for pair in input.split(',') {
let pair = pair.trim();
if pair.is_empty() {
continue;
}
let (id, addr) = pair
.split_once('=')
.ok_or_else(|| format!("bad peer entry {pair:?}, expected id=host:port"))?;
let id: u64 = id
.trim()
.parse()
.map_err(|_| format!("bad peer id in {pair:?}"))?;
out.insert(id, addr.trim().to_string());
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_peer_map_reads_id_host_port_pairs() {
let map = parse_peer_map("1=127.0.0.1:5001, 2=127.0.0.1:5002").unwrap();
assert_eq!(map.get(&1).map(String::as_str), Some("127.0.0.1:5001"));
assert_eq!(map.get(&2).map(String::as_str), Some("127.0.0.1:5002"));
}
#[test]
fn parse_peer_map_rejects_entry_without_equals() {
let err = parse_peer_map("1=127.0.0.1:5001,garbage").unwrap_err();
assert!(err.contains("expected id=host:port"), "got: {err}");
}
#[test]
fn parse_peer_map_skips_blank_entries() {
let map = parse_peer_map("1=a:1,,2=b:2,").unwrap();
assert_eq!(map.len(), 2);
}
#[test]
fn raft_tuning_defaults_match_the_example_values() {
let tuning = RaftTuning::default();
assert_eq!(tuning.heartbeat_ms, 250);
assert_eq!(tuning.election_min_ms, 1_000);
assert_eq!(tuning.election_max_ms, 2_000);
}
}