praxis_core/config/cluster/
endpoint.rs1use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
24#[serde(untagged)]
25pub enum Endpoint {
26 Simple(String),
28
29 Weighted {
31 address: String,
33
34 #[serde(default = "default_weight")]
37 weight: u32,
38 },
39}
40
41fn default_weight() -> u32 {
43 1
44}
45
46impl Endpoint {
47 pub fn address(&self) -> &str {
56 match self {
57 Self::Simple(s) => s,
58 Self::Weighted { address, .. } => address,
59 }
60 }
61
62 pub fn weight(&self) -> u32 {
71 match self {
72 Self::Simple(_) => 1,
73 Self::Weighted { weight, .. } => *weight,
74 }
75 }
76}
77
78impl From<String> for Endpoint {
79 fn from(s: String) -> Self {
80 Self::Simple(s)
81 }
82}
83
84impl From<&str> for Endpoint {
85 fn from(s: &str) -> Self {
86 Self::Simple(s.to_owned())
87 }
88}
89
90#[cfg(test)]
95#[expect(clippy::allow_attributes, reason = "blanket test suppressions")]
96#[allow(
97 clippy::unwrap_used,
98 clippy::expect_used,
99 clippy::indexing_slicing,
100 clippy::needless_raw_strings,
101 clippy::needless_raw_string_hashes,
102 reason = "tests use unwrap/expect/indexing/raw strings for brevity"
103)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn simple_endpoint_has_weight_one() {
109 let ep: Endpoint = "10.0.0.1:8080".into();
110 assert_eq!(ep.address(), "10.0.0.1:8080", "simple endpoint address mismatch");
111 assert_eq!(ep.weight(), 1, "simple endpoint should default to weight 1");
112 }
113
114 #[test]
115 fn weighted_endpoint_preserves_weight() {
116 let yaml = r#"
117address: "10.0.0.2:8080"
118weight: 3
119"#;
120 let ep: Endpoint = serde_yaml::from_str(yaml).unwrap();
121 assert_eq!(ep.address(), "10.0.0.2:8080", "weighted endpoint address mismatch");
122 assert_eq!(ep.weight(), 3, "weighted endpoint should preserve configured weight");
123 }
124
125 #[test]
126 fn weighted_endpoint_defaults_weight_to_one() {
127 let yaml = "address: \"10.0.0.1:80\"";
128 let ep: Endpoint = serde_yaml::from_str(yaml).unwrap();
129 assert_eq!(ep.weight(), 1, "omitted weight should default to 1");
130 }
131
132 #[test]
133 fn from_string() {
134 let ep = Endpoint::from("10.0.0.1:80".to_owned());
135 assert_eq!(ep.address(), "10.0.0.1:80", "From<String> should preserve address");
136 }
137
138 #[test]
139 fn parse_mixed_list() {
140 let yaml = r#"
141- "10.0.0.1:8080"
142- address: "10.0.0.2:8080"
143 weight: 3
144"#;
145 let eps: Vec<Endpoint> = serde_yaml::from_str(yaml).unwrap();
146 assert_eq!(eps.len(), 2, "mixed list should parse two endpoints");
147 assert_eq!(eps[0].weight(), 1, "simple entry should have weight 1");
148 assert_eq!(eps[1].weight(), 3, "weighted entry should have weight 3");
149 }
150}