use std::cmp::Ordering;
use std::fmt;
use std::ops::Deref;
use std::ops::DerefMut;
use display_more::DisplayOptionExt;
use crate::NodeId;
use crate::vote::LeaderIdCompare;
use crate::vote::RaftLeaderId;
use crate::vote::RaftTerm;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound = ""))]
pub struct LeaderId<Term, NID>
where
Term: RaftTerm,
NID: NodeId,
{
pub term: Term,
pub voted_for: Option<NID>,
}
impl<Term, NID> PartialOrd for LeaderId<Term, NID>
where
Term: RaftTerm,
NID: NodeId,
{
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
LeaderIdCompare::std(self, other)
}
}
impl<Term, NID> fmt::Display for LeaderId<Term, NID>
where
Term: RaftTerm,
NID: NodeId,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "T{}-N{}", self.term, self.voted_for.display())
}
}
impl<Term, NID> PartialEq<CommittedLeaderId<Term>> for LeaderId<Term, NID>
where
Term: RaftTerm,
NID: NodeId,
{
fn eq(&self, _other: &CommittedLeaderId<Term>) -> bool {
false
}
}
impl<Term, NID> PartialOrd<CommittedLeaderId<Term>> for LeaderId<Term, NID>
where
Term: RaftTerm,
NID: NodeId,
{
fn partial_cmp(&self, other: &CommittedLeaderId<Term>) -> Option<Ordering> {
if self.term == other.term {
Some(Ordering::Less)
} else {
self.term.partial_cmp(&other.term)
}
}
}
impl<Term, NID> PartialEq<LeaderId<Term, NID>> for CommittedLeaderId<Term>
where
Term: RaftTerm,
NID: NodeId,
{
fn eq(&self, _other: &LeaderId<Term, NID>) -> bool {
false
}
}
impl<Term, NID> PartialOrd<LeaderId<Term, NID>> for CommittedLeaderId<Term>
where
Term: RaftTerm,
NID: NodeId,
{
fn partial_cmp(&self, other: &LeaderId<Term, NID>) -> Option<Ordering> {
if self.term == other.term {
Some(Ordering::Greater)
} else {
self.term.partial_cmp(&other.term)
}
}
}
impl<Term, NID> RaftLeaderId for LeaderId<Term, NID>
where
Term: RaftTerm,
NID: NodeId,
{
type Term = Term;
type NodeId = NID;
type Committed = CommittedLeaderId<Term>;
fn new(term: Term, node_id: NID) -> Self {
Self {
term,
voted_for: Some(node_id),
}
}
fn term(&self) -> Term {
self.term
}
fn node_id(&self) -> &NID {
self.voted_for.as_ref().unwrap()
}
fn to_committed(&self) -> Self::Committed {
CommittedLeaderId::new(self.term)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(PartialOrd, Ord)]
#[derive(derive_more::Display)]
#[display("{}", term)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound = ""))]
#[cfg_attr(feature = "serde", serde(transparent))]
#[repr(transparent)]
pub struct CommittedLeaderId<Term>
where Term: RaftTerm
{
pub term: Term,
}
impl<Term> CommittedLeaderId<Term>
where Term: RaftTerm
{
pub fn new(term: Term) -> Self {
Self { term }
}
}
impl<Term> Deref for CommittedLeaderId<Term>
where Term: RaftTerm
{
type Target = Term;
fn deref(&self) -> &Self::Target {
&self.term
}
}
impl<Term> DerefMut for CommittedLeaderId<Term>
where Term: RaftTerm
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.term
}
}
#[rustfmt::skip]
mod impl_from_int {
use crate::vote::leader_id_std::CommittedLeaderId;
impl From<u64> for CommittedLeaderId<u64> {fn from(term: u64) -> Self {Self {term}}}
impl From<u32> for CommittedLeaderId<u32> {fn from(term: u32) -> Self {Self {term}}}
impl From<u16> for CommittedLeaderId<u16> {fn from(term: u16) -> Self {Self {term}}}
impl From<u8> for CommittedLeaderId<u8> {fn from(term: u8) -> Self {Self {term}}}
impl From<i64> for CommittedLeaderId<i64> {fn from(term: i64) -> Self {Self {term}}}
impl From<i32> for CommittedLeaderId<i32> {fn from(term: i32) -> Self {Self {term}}}
impl From<i16> for CommittedLeaderId<i16> {fn from(term: i16) -> Self {Self {term}}}
impl From<i8> for CommittedLeaderId<i8> {fn from(term: i8) -> Self {Self {term}}}
}
#[cfg(test)]
#[allow(clippy::nonminimal_bool)]
mod tests {
use super::LeaderId;
use crate::vote::RaftLeaderId;
#[cfg(feature = "serde")]
#[test]
fn test_committed_leader_id_serde() -> anyhow::Result<()> {
use super::CommittedLeaderId;
let c = CommittedLeaderId::<u64>::new(5);
let s = serde_json::to_string(&c)?;
assert_eq!(r#"5"#, s);
let c2: CommittedLeaderId<u64> = serde_json::from_str(&s)?;
assert_eq!(CommittedLeaderId::new(5), c2);
Ok(())
}
#[test]
#[allow(clippy::neg_cmp_op_on_partial_ord)]
fn test_std_leader_id_partial_order() -> anyhow::Result<()> {
#[allow(clippy::redundant_closure)]
let lid = |term, node_id| LeaderId::<u64, u64>::new(term, node_id);
assert!(lid(2, 2) > lid(1, 2));
assert!(lid(1, 2) < lid(2, 2));
assert!(lid(2, 2) == lid(2, 2));
assert!(lid(2, 2) >= lid(2, 2));
assert!(lid(2, 2) <= lid(2, 2));
assert!(!(lid(2, 2) > lid(2, 3)));
assert!(!(lid(2, 2) < lid(2, 3)));
assert!(!(lid(2, 2) == lid(2, 3)));
Ok(())
}
#[test]
#[allow(clippy::neg_cmp_op_on_partial_ord)]
fn test_leader_id_vs_committed_partial_order() -> anyhow::Result<()> {
use super::CommittedLeaderId;
let lid = |term, node_id| LeaderId::<u64, u64>::new(term, node_id);
let clid = |term| CommittedLeaderId::<u64>::new(term);
assert!(lid(2, 2) != clid(2));
assert!(lid(2, 2) < clid(2));
assert!(!(lid(2, 2) > clid(2)));
assert!(lid(1, 2) < clid(2));
assert!(lid(3, 2) > clid(2));
Ok(())
}
#[test]
#[allow(clippy::neg_cmp_op_on_partial_ord)]
fn test_committed_vs_leader_id_partial_order() -> anyhow::Result<()> {
use super::CommittedLeaderId;
let lid = |term, node_id| LeaderId::<u64, u64>::new(term, node_id);
let clid = |term| CommittedLeaderId::<u64>::new(term);
assert!(clid(2) != lid(2, 2));
assert!(clid(2) != lid(2, 5));
assert!(clid(2) > lid(2, 2));
assert!(!(clid(2) < lid(2, 2)));
assert!(!(clid(2) == lid(2, 2)));
assert!(clid(2) > lid(1, 2));
assert!(clid(2) < lid(3, 2));
assert!(!(clid(2) > lid(3, 2)));
assert!(!(clid(2) == lid(1, 2)));
Ok(())
}
#[test]
fn test_committed_leader_id_deref() -> anyhow::Result<()> {
use super::CommittedLeaderId;
let clid = CommittedLeaderId::<u64>::new(5);
assert_eq!(5, *clid);
let term_ref: &u64 = &clid;
assert_eq!(&5, term_ref);
Ok(())
}
#[test]
fn test_committed_leader_id_deref_mut() -> anyhow::Result<()> {
use super::CommittedLeaderId;
let mut clid = CommittedLeaderId::<u64>::new(5);
*clid = 10;
assert_eq!(10, *clid);
assert_eq!(CommittedLeaderId::new(10), clid);
Ok(())
}
#[test]
fn test_committed_leader_id_from_int() -> anyhow::Result<()> {
use super::CommittedLeaderId;
macro_rules! test_from {
($term_type:ty, $value:expr) => {{
let clid: CommittedLeaderId<$term_type> = $value.into();
assert_eq!(CommittedLeaderId::new($value), clid);
let clid = CommittedLeaderId::<$term_type>::from($value);
assert_eq!(CommittedLeaderId::new($value), clid);
}};
}
test_from!(u64, 5u64);
test_from!(u32, 5u32);
test_from!(u16, 5u16);
test_from!(u8, 5u8);
test_from!(i64, 5i64);
test_from!(i32, 5i32);
test_from!(i16, 5i16);
test_from!(i8, 5i8);
Ok(())
}
}