libraft 0.1.1

raft implementation in rust.
Documentation
use std::u64;

use raftpb::{Entry, EntryType, MessageType};

use protobuf::Message;

pub const NO_LIMIT: u64 = u64::MAX;

pub fn limit_size<T: Message + Clone>(entries: &mut Vec<T>, max: u64) {
    if max == NO_LIMIT || entries.len() <= 1 {
        return;
    }

    let mut size = 0;
    let limit = entries
        .iter()
        .take_while(|&e| {
            if size == 0 {
                size += u64::from(Message::compute_size(e));
                true
            } else {
                size += u64::from(Message::compute_size(e));
                size <= max
            }
        })
        .count();

    entries.truncate(limit);
}

pub fn num_of_pending_conf(ents: &[Entry]) -> u64 {
    ents.into_iter()
        .filter(|e| e.get_entry_type() == EntryType::EntryConfChange)
        .count() as u64
}

pub fn vote_msg_resp_type(t: MessageType) -> MessageType {
    match t {
        MessageType::MsgVote => MessageType::MsgVoteResp,
        MessageType::MsgPreVote => MessageType::MsgPreVoteResp,
        _ => panic!("not a vote message: {:?}", t),
    }
}

pub fn is_local_msg(msgt: MessageType) -> bool {
    msgt == MessageType::MsgHup
        || msgt == MessageType::MsgBeat
        || msgt == MessageType::MsgUnreachable
        || msgt == MessageType::MsgSnapStatus
        || msgt == MessageType::MsgCheckQuorum
}

pub fn is_response_msg(msgt: MessageType) -> bool {
    msgt == MessageType::MsgAppResp
        || msgt == MessageType::MsgVoteResp
        || msgt == MessageType::MsgHeartbeatResp
        || msgt == MessageType::MsgPreVoteResp
        || msgt == MessageType::MsgUnreachable
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_is_local_msg() {
        let tests = vec![
            (MessageType::MsgHup, true),
            (MessageType::MsgBeat, true),
            (MessageType::MsgUnreachable, true),
            (MessageType::MsgSnapStatus, true),
            (MessageType::MsgCheckQuorum, true),
            (MessageType::MsgTransferLeader, false),
            (MessageType::MsgProp, false),
            (MessageType::MsgApp, false),
            (MessageType::MsgAppResp, false),
            (MessageType::MsgVote, false),
            (MessageType::MsgVoteResp, false),
            (MessageType::MsgSnap, false),
            (MessageType::MsgHeartbeat, false),
            (MessageType::MsgHeartbeatResp, false),
            (MessageType::MsgTimeoutNow, false),
            (MessageType::MsgReadIndex, false),
            (MessageType::MsgReadIndexResp, false),
            (MessageType::MsgPreVote, false),
            (MessageType::MsgPreVoteResp, false),
        ];

        for (msgt, is_local) in tests {
            assert_eq!(is_local_msg(msgt), is_local);
        }
    }

    #[test]
    fn test_is_response_msg() {
        let tests = vec![
            (MessageType::MsgHeartbeatResp, true),
            (MessageType::MsgUnreachable, true),
            (MessageType::MsgVoteResp, true),
            (MessageType::MsgPreVoteResp, true),
            (MessageType::MsgAppResp, true),
            (MessageType::MsgHup, false),
            (MessageType::MsgBeat, false),
            (MessageType::MsgSnapStatus, false),
            (MessageType::MsgCheckQuorum, false),
            (MessageType::MsgTransferLeader, false),
            (MessageType::MsgProp, false),
            (MessageType::MsgApp, false),
            (MessageType::MsgVote, false),
            (MessageType::MsgSnap, false),
            (MessageType::MsgHeartbeat, false),
            (MessageType::MsgTimeoutNow, false),
            (MessageType::MsgReadIndex, false),
            (MessageType::MsgReadIndexResp, false),
            (MessageType::MsgPreVote, false),
        ];

        for (msgt, is_local) in tests {
            assert_eq!(is_response_msg(msgt), is_local);
        }
    }

    #[test]
    fn test_vote_msg_resp_type() {
        assert_eq!(
            vote_msg_resp_type(MessageType::MsgVote),
            MessageType::MsgVoteResp
        );

        assert_eq!(
            vote_msg_resp_type(MessageType::MsgPreVote),
            MessageType::MsgPreVoteResp
        );
    }

    #[test]
    fn test_limit_size() {
        let ents = vec![new_entry(4, 4), new_entry(5, 5), new_entry(6, 6)];
        let tests = vec![
            (
                u64::MAX,
                vec![new_entry(4, 4), new_entry(5, 5), new_entry(6, 6)],
            ),
            (0, vec![new_entry(4, 4)]),
            (
                Message::compute_size(&ents[0]) as u64 + Message::compute_size(&ents[1]) as u64,
                vec![new_entry(4, 4), new_entry(5, 5)],
            ),
            (
                Message::compute_size(&ents[0]) as u64
                    + Message::compute_size(&ents[1]) as u64
                    + Message::compute_size(&ents[2]) as u64 - 1,
                vec![new_entry(4, 4), new_entry(5, 5)],
            ),
            (
                Message::compute_size(&ents[0]) as u64 + Message::compute_size(&ents[1]) as u64 - 1,
                vec![new_entry(4, 4)],
            ),
        ];

        for (max_size, wents) in tests {
            let mut ents = vec![new_entry(4, 4), new_entry(5, 5), new_entry(6, 6)];
            limit_size(&mut ents, max_size);
            assert_eq!(ents, wents);
        }
    }

    fn new_entry(index: u64, term: u64) -> Entry {
        let mut e = Entry::new();
        e.set_index(index);
        e.set_term(term);
        e
    }
}