fn check_invariants(cfg: &Config, prs: &ProgressMap) -> Result<(), String> {
    // NB: intentionally allow the empty config. In production we'll never see a
    // non-empty config(we prevent it from being created) but we will need to
    // be able *create* an initial config, for example during bootstrap (or
    // during tests). Instead of having to hand-code this, we allow
    // transaction from an empty config into any other legal and no-empty
    // config.
    let mut sets = HashSet::new();
    sets.extend(cfg.voters.incoming.votes.iter());
    sets.extend(cfg.voters.outgoing.votes.iter());
    sets.extend(cfg.learners.iter());
    sets.extend(cfg.learners_next.iter());
    for id in sets.iter() {
        if !prs.contains_key(id) {
            return Err(format!("no progress for {}", id));
        }
    }

    // TODO: Why?
    // Any staged learner was staged because it could not be directly added due
    // to a conflicting voter in the outgoing config.
    for id in cfg.learners_next.iter() {
        if !cfg.voters.outgoing.contains(id) {
            return Err(format!("{} is in learners_next, but not outgoing", id));
        }
        if prs.get(id).as_ref().unwrap().is_learner {
            return Err(format!(
                "{} is in learners_next, but is already marked as learner",
                id
            ));
        }
    }

    // conversely leaners and voters doesn't intersect at all.
    for id in cfg.learners.iter() {
        if cfg.voters.outgoing.contains(id) {
            return Err(format!("{} is in Leaners and outgoing", id));
        }
        if cfg.voters.incoming.contains(id) {
            return Err(format!("{} is in Leaners and incoming", id));
        }
        if !prs.get(id).as_ref().unwrap().is_learner {
            return Err(format!(
                "{} is in Learners, but is not marked as leaner",
                id
            ));
        }
    }

    if !cfg.voters.joint() {
        // We enforce that empty maps are nil instead of zero.
        if !cfg.voters.outgoing.is_empty() {
            return Err("outgoing must be nil when not joint".to_string());
        }
        if !cfg.learners_next.is_empty() {
            return Err("learners_next must be nil when not joint".to_string());
        }
        if cfg.auto_leave {
            return Err("auto_leave must be false when not joint".to_string());
        }
    }

    Ok(())
}
// makes sure that the config and progress are compatible with
// each other. This is used to check both what the Changer is initialized with.
// as well as it returns.
makes sure that the config and progress are compatible with
each other. This is used to check both what the Changer is initialized with.
as well as it returns.
makes sure that the config and progress are compatible with each other . This is used to check both what the Changer is initialized with . as well as it returns .
fn check_invariants(cfg: &Config, prs: &ProgressMap) -> Result<(), String> {
    // NB: intentionally allow the empty config. In production we'll never see a
    // non-empty config(we prevent it from being created) but we will need to
    // be able *create* an initial config, for example during bootstrap (or
    // during tests). Instead of having to hand-code this, we allow
    // transaction from an empty config into any other legal and no-empty
    // config.
    let mut sets = HashSet::new();
    sets.extend(cfg.voters.incoming.votes.iter());
    sets.extend(cfg.voters.outgoing.votes.iter());
    sets.extend(cfg.learners.iter());
    sets.extend(cfg.learners_next.iter());
    for id in sets.iter() {
        if !prs.contains_key(id) {
            return Err(format!("no progress for {}", id));
        }
    }
    // TODO: Why?
    // Any staged learner was staged because it could not be directly added due
    // to a conflicting voter in the outgoing config.
    for id in cfg.learners_next.iter() {
        if !cfg.voters.outgoing.contains(id) {
            return Err(format!("{} is in learners_next, but not outgoing", id));
        }
        if prs.get(id).as_ref().unwrap().is_learner {
            return Err(format!(
                "{} is in learners_next, but is already marked as learner",
                id
            ));
        }
    }
    // conversely leaners and voters doesn't intersect at all.
    for id in cfg.learners.iter() {
        if cfg.voters.outgoing.contains(id) {
            return Err(format!("{} is in Leaners and outgoing", id));
        }
        if cfg.voters.incoming.contains(id) {
            return Err(format!("{} is in Leaners and incoming", id));
        }
        if !prs.get(id).as_ref().unwrap().is_learner {
            return Err(format!(
                "{} is in Learners, but is not marked as leaner",
                id
            ));
        }
    }
    if !cfg.voters.joint() {
        // We enforce that empty maps are nil instead of zero.
        if !cfg.voters.outgoing.is_empty() {
            return Err("outgoing must be nil when not joint".to_string());
        }
        if !cfg.learners_next.is_empty() {
            return Err("learners_next must be nil when not joint".to_string());
        }
        if cfg.auto_leave {
            return Err("auto_leave must be false when not joint".to_string());
        }
    }
    Ok(())
}
fn check_invariants ( cfg : & Config , prs : & ProgressMap ) -> Result < ( ) , String > { // NB: intentionally allow the empty config. In production we'll never see a // non-empty config(we prevent it from being created) but we will need to // be able *create* an initial config, for example during bootstrap (or // during tests). Instead of having to hand-code this, we allow // transaction from an empty config into any other legal and no-empty // config. let mut sets = HashSet :: new ( ) ; sets . extend ( cfg . voters . incoming . votes . iter ( ) ) ; sets . extend ( cfg . voters . outgoing . votes . iter ( ) ) ; sets . extend ( cfg . learners . iter ( ) ) ; sets . extend ( cfg . learners_next . iter ( ) ) ; for id in sets . iter ( ) { if ! prs . contains_key ( id ) { return Err ( format ! ( " " id ) ) ; } } // TODO: Why? // Any staged learner was staged because it could not be directly added due // to a conflicting voter in the outgoing config. for id in cfg . learners_next . iter ( ) { if ! cfg . voters . outgoing . contains ( id ) { return Err ( format ! ( " " id ) ) ; } if prs . get ( id ) . as_ref ( ) . unwrap ( ) . is_learner { return Err ( format ! ( " " id ) ) ; } } // conversely leaners and voters doesn't intersect at all. for id in cfg . learners . iter ( ) { if cfg . voters . outgoing . contains ( id ) { return Err ( format ! ( " " id ) ) ; } if cfg . voters . incoming . contains ( id ) { return Err ( format ! ( " " id ) ) ; } if ! prs . get ( id ) . as_ref ( ) . unwrap ( ) . is_learner { return Err ( format ! ( " " id ) ) ; } } if ! cfg . voters . joint ( ) { // We enforce that empty maps are nil instead of zero. if ! cfg . voters . outgoing . is_empty ( ) { return Err ( " " . to_string ( ) ) ; } if ! cfg . learners_next . is_empty ( ) { return Err ( " " . to_string ( ) ) ; } if cfg . auto_leave { return Err ( " " . to_string ( ) ) ; } } Ok ( ( ) ) }
makes sure that the config and progress are compatible with
each other.
makes sure that the config and progress are compatible with each other .
cfg
&Config
prs
&ProgressMap