use crate::eraftpb::{
ConfChange, ConfChangeSingle, ConfChangeTransition, ConfChangeType, ConfChangeV2,
};
use std::borrow::Cow;
use std::fmt::Write;
pub fn new_conf_change_single(node_id: u64, ty: ConfChangeType) -> ConfChangeSingle {
let mut single = ConfChangeSingle::default();
single.node_id = node_id;
single.set_change_type(ty);
single
}
pub fn parse_conf_change(s: &str) -> Result<Vec<ConfChangeSingle>, String> {
let s = s.trim();
if s.is_empty() {
return Ok(vec![]);
}
let mut ccs = vec![];
let splits = s.split_ascii_whitespace();
for tok in splits {
if tok.len() < 2 {
return Err(format!("unknown token {}", tok));
}
let mut cc = ConfChangeSingle::default();
let mut chars = tok.chars();
cc.set_change_type(match chars.next().unwrap() {
'v' => ConfChangeType::AddNode,
'l' => ConfChangeType::AddLearnerNode,
'r' => ConfChangeType::RemoveNode,
_ => return Err(format!("unknown token {}", tok)),
});
cc.node_id = match chars.as_str().parse() {
Ok(id) => id,
Err(e) => return Err(format!("parse token {} fail: {}", tok, e)),
};
ccs.push(cc);
}
Ok(ccs)
}
pub fn stringify_conf_change(ccs: &[ConfChangeSingle]) -> String {
let mut s = String::new();
for (i, cc) in ccs.iter().enumerate() {
if i > 0 {
s.push(' ');
}
match cc.get_change_type() {
ConfChangeType::AddNode => s.push('v'),
ConfChangeType::AddLearnerNode => s.push('l'),
ConfChangeType::RemoveNode => s.push('r'),
}
write!(&mut s, "{}", cc.node_id).unwrap();
}
s
}
pub trait ConfChangeI {
fn into_v2(self) -> ConfChangeV2;
fn as_v2(&self) -> Cow<ConfChangeV2>;
fn as_v1(&self) -> Option<&ConfChange>;
}
impl ConfChangeI for ConfChange {
#[inline]
fn into_v2(mut self) -> ConfChangeV2 {
let mut cc = ConfChangeV2::default();
let single = new_conf_change_single(self.node_id, self.get_change_type());
cc.mut_changes().push(single);
cc.set_context(self.take_context());
cc
}
#[inline]
fn as_v2(&self) -> Cow<ConfChangeV2> {
Cow::Owned(self.clone().into_v2())
}
#[inline]
fn as_v1(&self) -> Option<&ConfChange> {
Some(self)
}
}
impl ConfChangeI for ConfChangeV2 {
#[inline]
fn into_v2(self) -> ConfChangeV2 {
self
}
#[inline]
fn as_v2(&self) -> Cow<ConfChangeV2> {
Cow::Borrowed(self)
}
#[inline]
fn as_v1(&self) -> Option<&ConfChange> {
None
}
}
impl ConfChangeV2 {
pub fn enter_joint(&self) -> Option<bool> {
if self.get_transition() != ConfChangeTransition::Auto || self.changes.len() > 1 {
match self.get_transition() {
ConfChangeTransition::Auto | ConfChangeTransition::Implicit => Some(true),
ConfChangeTransition::Explicit => Some(false),
}
} else {
None
}
}
pub fn leave_joint(&self) -> bool {
self.get_transition() == ConfChangeTransition::Auto && self.changes.is_empty()
}
}