fix(#3124): adapt hive-c0re's converge loop to a Copy AgentState

Making `AgentState` `Copy` in the preceding commit is a change to every
consumer of the type, not to the crate that declares it. `hive-c0re` grew its
own consumer while this branch was in review, and under `-D pedantic` a
one-byte enum taken by reference is `trivially_copy_pass_by_ref` and a
`.clone()` on it is `clone_on_copy`. Neither crate is wrong alone; the merge
is.

`decide` now takes the state by value and the call sites drop the `&`. No
behaviour change — the function only matches on the value.
This commit is contained in:
atlas 2026-09-02 02:48:39 +02:00
commit 8dbccd5578

View file

@ -171,7 +171,7 @@ fn plan(
let Some(intent) = intents.get(agent) else {
continue;
};
match decide(&decl.state, present.contains(agent), *intent) {
match decide(decl.state, present.contains(agent), *intent) {
Converge::Deploy => plan.deploy.push(agent.clone()),
Converge::Start => plan.start.push(agent.clone()),
Converge::Stop => plan.stop.push(agent.clone()),
@ -196,7 +196,7 @@ enum Converge {
/// The whole decision, pure: no queue, no store, no container. The three
/// inputs are exactly what [`converge`] reads per agent, so the table below is
/// the behaviour rather than a model of it.
fn decide(state: &AgentState, present: bool, intent: Option<Wanted>) -> Converge {
fn decide(state: AgentState, present: bool, intent: Option<Wanted>) -> Converge {
match state {
AgentState::Up if !present => Converge::Deploy,
AgentState::Up => {
@ -231,14 +231,7 @@ mod tests {
HiveWanted {
agents: agents
.iter()
.map(|(name, state)| {
(
(*name).to_owned(),
AgentWanted {
state: state.clone(),
},
)
})
.map(|(name, state)| ((*name).to_owned(), AgentWanted { state: *state }))
.collect(),
}
}
@ -292,10 +285,10 @@ mod tests {
#[test]
fn a_declared_agent_this_hive_does_not_have_is_deployed() {
assert_eq!(decide(&AgentState::Up, false, None), Converge::Deploy);
assert_eq!(decide(AgentState::Up, false, None), Converge::Deploy);
// Even holding an intent: a row can outlive its container.
assert_eq!(
decide(&AgentState::Up, false, Some(Wanted::Up)),
decide(AgentState::Up, false, Some(Wanted::Up)),
Converge::Deploy
);
}
@ -303,11 +296,11 @@ mod tests {
#[test]
fn a_hive_that_already_agrees_queues_nothing() {
assert_eq!(
decide(&AgentState::Up, true, Some(Wanted::Up)),
decide(AgentState::Up, true, Some(Wanted::Up)),
Converge::Nothing
);
assert_eq!(
decide(&AgentState::Offline, true, Some(Wanted::Offline)),
decide(AgentState::Offline, true, Some(Wanted::Offline)),
Converge::Nothing
);
}
@ -315,11 +308,11 @@ mod tests {
#[test]
fn a_disagreeing_intent_is_converged_both_ways() {
assert_eq!(
decide(&AgentState::Up, true, Some(Wanted::Offline)),
decide(AgentState::Up, true, Some(Wanted::Offline)),
Converge::Start
);
assert_eq!(
decide(&AgentState::Offline, true, Some(Wanted::Up)),
decide(AgentState::Offline, true, Some(Wanted::Up)),
Converge::Stop
);
}
@ -329,15 +322,15 @@ mod tests {
/// to be doing.
#[test]
fn an_agent_with_no_intent_row_is_converged() {
assert_eq!(decide(&AgentState::Up, true, None), Converge::Start);
assert_eq!(decide(&AgentState::Offline, true, None), Converge::Stop);
assert_eq!(decide(AgentState::Up, true, None), Converge::Start);
assert_eq!(decide(AgentState::Offline, true, None), Converge::Stop);
}
/// Declared offline, no container: the one absent case that must not
/// deploy. Its control is the `Up` case above, which must.
#[test]
fn an_absent_agent_declared_offline_is_left_absent() {
assert_eq!(decide(&AgentState::Offline, false, None), Converge::Nothing);
assert_eq!(decide(AgentState::Offline, false, None), Converge::Nothing);
}
/// Version skew is handled one layer up, at the decode: `AgentState` is
@ -350,8 +343,8 @@ mod tests {
fn every_state_this_build_knows_is_covered_above() {
for state in [AgentState::Up, AgentState::Offline] {
let seen = [true, false].iter().any(|present| {
decide(&state, *present, None) != Converge::Nothing
|| decide(&state, *present, Some(Wanted::Up)) != Converge::Nothing
decide(state, *present, None) != Converge::Nothing
|| decide(state, *present, Some(Wanted::Up)) != Converge::Nothing
});
assert!(seen, "{state:?} produces no action in any combination");
}