use tracing::warn;
use crate::models::SolverConfig;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Recipients {
Targeted(String),
Broadcast(Vec<String>),
}
pub fn resolve_recipients(solvers: &[SolverConfig], assigned_solver: Option<&str>) -> Recipients {
let all: Vec<String> = solvers.iter().map(|s| s.pubkey.clone()).collect();
match assigned_solver {
Some(pk) if solvers.iter().any(|s| s.pubkey == pk) => Recipients::Targeted(pk.to_string()),
Some(pk) => {
warn!(
assigned_solver = %pk,
configured_count = solvers.len(),
"assigned_solver is not in configured solvers list; falling back to broadcast"
);
Recipients::Broadcast(all)
}
None => Recipients::Broadcast(all),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::models::SolverPermission;
fn solver(pk: &str) -> SolverConfig {
SolverConfig {
pubkey: pk.into(),
permission: SolverPermission::Read,
}
}
#[test]
fn targeted_when_assigned_solver_is_configured() {
let solvers = [solver("pk-a"), solver("pk-b")];
let out = resolve_recipients(&solvers, Some("pk-a"));
assert_eq!(out, Recipients::Targeted("pk-a".into()));
}
#[test]
fn falls_back_to_broadcast_when_assigned_solver_unknown() {
let solvers = [solver("pk-a"), solver("pk-b")];
let out = resolve_recipients(&solvers, Some("pk-unknown"));
assert_eq!(
out,
Recipients::Broadcast(vec!["pk-a".into(), "pk-b".into()])
);
}
#[test]
fn broadcast_when_no_assigned_solver() {
let solvers = [solver("pk-a"), solver("pk-b")];
let out = resolve_recipients(&solvers, None);
assert_eq!(
out,
Recipients::Broadcast(vec!["pk-a".into(), "pk-b".into()])
);
}
#[test]
fn empty_solvers_with_no_assignment_returns_empty_broadcast() {
let out = resolve_recipients(&[], None);
assert_eq!(out, Recipients::Broadcast(vec![]));
}
#[test]
fn empty_solvers_with_assignment_falls_back_to_empty_broadcast() {
let out = resolve_recipients(&[], Some("pk-a"));
assert_eq!(out, Recipients::Broadcast(vec![]));
}
}