From 8dbccd557810f80ff9933966dd60a345a15467d6 Mon Sep 17 00:00:00 2001 From: atlas Date: Wed, 2 Sep 2026 02:48:39 +0200 Subject: [PATCH] fix(#3124): adapt hive-c0re's converge loop to a Copy AgentState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- hive-c0re/src/workers/wanted.rs | 35 +++++++++++++-------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/hive-c0re/src/workers/wanted.rs b/hive-c0re/src/workers/wanted.rs index 760a40fb..6fd39393 100644 --- a/hive-c0re/src/workers/wanted.rs +++ b/hive-c0re/src/workers/wanted.rs @@ -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) -> Converge { +fn decide(state: AgentState, present: bool, intent: Option) -> 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"); }