use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub enum SubscriptionBalancerStrategy {
LeastLoad,
LeastPing,
#[default]
Random,
RoundRobin,
#[serde(other)]
Unknown,
}
impl SubscriptionBalancerStrategy {
pub(crate) const fn as_str(self) -> Option<&'static str> {
match self {
Self::LeastLoad => Some("leastLoad"),
Self::LeastPing => Some("leastPing"),
Self::Random => Some("random"),
Self::RoundRobin => Some("roundRobin"),
Self::Unknown => None,
}
}
}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(default, rename_all = "camelCase")]
pub struct SubscriptionBalancer {
pub id: i64,
pub remark: String,
pub strategy: SubscriptionBalancerStrategy,
pub inbound_ids: Vec<i64>,
#[serde(default, deserialize_with = "deserialize_weights")]
pub member_weights: std::collections::BTreeMap<i64, f64>,
pub sort_order: i32,
pub enabled: bool,
pub created_at: i64,
pub updated_at: i64,
}
#[derive(Clone, Debug, PartialEq)]
pub struct SubscriptionBalancerInput {
pub remark: String,
pub strategy: SubscriptionBalancerStrategy,
pub inbound_ids: Vec<i64>,
pub member_weights: std::collections::BTreeMap<i64, f64>,
pub sort_order: i32,
pub enabled: Option<bool>,
}
impl SubscriptionBalancerInput {
pub fn new(remark: impl Into<String>, inbound_ids: Vec<i64>) -> Self {
Self {
remark: remark.into(),
strategy: SubscriptionBalancerStrategy::Random,
inbound_ids,
member_weights: std::collections::BTreeMap::new(),
sort_order: 1,
enabled: Some(true),
}
}
}
fn deserialize_weights<'de, D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<std::collections::BTreeMap<i64, f64>, D::Error> {
Option::<std::collections::BTreeMap<i64, f64>>::deserialize(deserializer)
.map(Option::unwrap_or_default)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn strategies_and_constructor_match_wire_defaults() {
assert_eq!(
SubscriptionBalancerStrategy::LeastLoad.as_str(),
Some("leastLoad")
);
assert_eq!(
SubscriptionBalancerStrategy::LeastPing.as_str(),
Some("leastPing")
);
assert_eq!(
SubscriptionBalancerStrategy::Random.as_str(),
Some("random")
);
assert_eq!(
SubscriptionBalancerStrategy::RoundRobin.as_str(),
Some("roundRobin")
);
assert_eq!(SubscriptionBalancerStrategy::Unknown.as_str(), None);
let input = SubscriptionBalancerInput::new("EU pool", vec![7, 9]);
assert_eq!(input.remark, "EU pool");
assert_eq!(input.strategy, SubscriptionBalancerStrategy::Random);
assert_eq!(input.inbound_ids, [7, 9]);
assert_eq!(input.sort_order, 1);
assert_eq!(input.enabled, Some(true));
}
}