use crate::models::{SolverConfig, SolverPermission};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Recipients {
Targeted(String),
Broadcast {
pubkeys: Vec<String>,
via_fallback: bool,
},
Unroutable,
}
pub fn resolve_recipients(
solvers: &[SolverConfig],
assigned_solver: Option<&str>,
fallback_to_all: bool,
) -> Recipients {
if let Some(pk) = assigned_solver {
if solvers
.iter()
.any(|s| s.pubkey == pk && s.permission == SolverPermission::Write)
{
return Recipients::Targeted(pk.to_string());
}
}
let write_pubkeys: Vec<String> = solvers
.iter()
.filter(|s| s.permission == SolverPermission::Write)
.map(|s| s.pubkey.clone())
.collect();
if !write_pubkeys.is_empty() {
return Recipients::Broadcast {
pubkeys: write_pubkeys,
via_fallback: false,
};
}
if fallback_to_all && !solvers.is_empty() {
let all_pubkeys: Vec<String> = solvers.iter().map(|s| s.pubkey.clone()).collect();
return Recipients::Broadcast {
pubkeys: all_pubkeys,
via_fallback: true,
};
}
Recipients::Unroutable
}
#[cfg(test)]
mod tests {
use super::*;
fn solver(pk: &str, perm: SolverPermission) -> SolverConfig {
SolverConfig {
pubkey: pk.to_string(),
permission: perm,
}
}
#[test]
fn targeted_write_assigned_solver_wins() {
let solvers = vec![
solver("pk-w1", SolverPermission::Write),
solver("pk-r1", SolverPermission::Read),
];
let got = resolve_recipients(&solvers, Some("pk-w1"), false);
assert_eq!(got, Recipients::Targeted("pk-w1".to_string()));
}
#[test]
fn assigned_solver_not_in_configured_falls_back_to_write_broadcast() {
let solvers = vec![
solver("pk-w1", SolverPermission::Write),
solver("pk-w2", SolverPermission::Write),
];
let got = resolve_recipients(&solvers, Some("pk-unknown"), false);
assert_eq!(
got,
Recipients::Broadcast {
pubkeys: vec!["pk-w1".to_string(), "pk-w2".to_string()],
via_fallback: false,
}
);
}
#[test]
fn read_permission_assigned_is_ignored_broadcast_to_write_set() {
let solvers = vec![
solver("pk-r1", SolverPermission::Read),
solver("pk-w1", SolverPermission::Write),
];
let got = resolve_recipients(&solvers, Some("pk-r1"), false);
assert_eq!(
got,
Recipients::Broadcast {
pubkeys: vec!["pk-w1".to_string()],
via_fallback: false,
}
);
}
#[test]
fn no_assignment_broadcasts_to_write_set() {
let solvers = vec![
solver("pk-w1", SolverPermission::Write),
solver("pk-w2", SolverPermission::Write),
solver("pk-r1", SolverPermission::Read),
];
let got = resolve_recipients(&solvers, None, false);
assert_eq!(
got,
Recipients::Broadcast {
pubkeys: vec!["pk-w1".to_string(), "pk-w2".to_string()],
via_fallback: false,
}
);
}
#[test]
fn zero_write_solvers_with_fallback_on_broadcasts_to_all() {
let solvers = vec![
solver("pk-r1", SolverPermission::Read),
solver("pk-r2", SolverPermission::Read),
];
let got = resolve_recipients(&solvers, None, true);
assert_eq!(
got,
Recipients::Broadcast {
pubkeys: vec!["pk-r1".to_string(), "pk-r2".to_string()],
via_fallback: true,
}
);
}
#[test]
fn zero_write_solvers_with_fallback_off_is_unroutable() {
let solvers = vec![
solver("pk-r1", SolverPermission::Read),
solver("pk-r2", SolverPermission::Read),
];
let got = resolve_recipients(&solvers, None, false);
assert_eq!(got, Recipients::Unroutable);
}
#[test]
fn zero_configured_solvers_with_fallback_off_is_unroutable() {
let got = resolve_recipients(&[], None, false);
assert_eq!(got, Recipients::Unroutable);
}
#[test]
fn zero_configured_solvers_with_fallback_on_is_unroutable() {
let got = resolve_recipients(&[], None, true);
assert_eq!(got, Recipients::Unroutable);
}
}