spire_core/identity/
identities.rs1use std::fmt;
5use std::sync::Arc;
6use std::time::Duration;
7
8use super::Identity;
9use crate::Result;
10
11pub(super) type Generate = Arc<dyn Fn(u64) -> Result<Identity> + Send + Sync>;
13
14#[derive(Clone)]
16pub(super) enum Source {
17 Fixed(Vec<Identity>),
19 Generate(Generate),
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub(super) enum Choice {
26 InTurn,
27 Random,
28 StickyPerHost,
29}
30
31#[derive(Clone)]
44pub struct Identities {
45 pub(super) source: Source,
46 pub(super) max_active: usize,
47 pub(super) choice: Choice,
48 pub(super) max_requests: Option<u64>,
49 pub(super) max_age: Option<Duration>,
50 pub(super) max_failures: u32,
51 pub(super) rest: Duration,
52}
53
54impl Identities {
55 pub const DEFAULT_MAX_ACTIVE: usize = 16;
58
59 #[must_use]
65 pub fn new(identities: impl IntoIterator<Item = Identity>) -> Self {
66 let identities: Vec<Identity> = identities.into_iter().collect();
67 assert!(
68 !identities.is_empty(),
69 "`Identities::new` needs an identity"
70 );
71 for (index, identity) in identities.iter().enumerate() {
72 assert!(
73 !identities[..index].contains(identity),
74 "two identities are named `{}`",
75 identity.name()
76 );
77 }
78 let max_active = identities.len();
79 Self::with_source(Source::Fixed(identities), max_active)
80 }
81
82 #[must_use]
91 pub fn from_fn<F>(generate: F) -> Self
92 where
93 F: Fn(u64) -> Result<Identity> + Send + Sync + 'static,
94 {
95 Self::with_source(
96 Source::Generate(Arc::new(generate)),
97 Self::DEFAULT_MAX_ACTIVE,
98 )
99 }
100
101 const fn with_source(source: Source, max_active: usize) -> Self {
102 Self {
103 source,
104 max_active,
105 choice: Choice::InTurn,
106 max_requests: None,
107 max_age: None,
108 max_failures: 3,
109 rest: Duration::from_mins(1),
110 }
111 }
112
113 #[must_use]
121 pub fn max_active(mut self, n: usize) -> Self {
122 assert!(n > 0, "`Identities::max_active` must be above zero");
123 if matches!(self.source, Source::Generate(_)) {
124 self.max_active = n;
125 }
126 self
127 }
128
129 #[must_use]
131 pub const fn in_turn(mut self) -> Self {
132 self.choice = Choice::InTurn;
133 self
134 }
135
136 #[must_use]
138 pub const fn at_random(mut self) -> Self {
139 self.choice = Choice::Random;
140 self
141 }
142
143 #[must_use]
147 pub const fn sticky_per_host(mut self) -> Self {
148 self.choice = Choice::StickyPerHost;
149 self
150 }
151
152 #[must_use]
155 pub const fn retire_after_requests(mut self, n: u64) -> Self {
156 self.max_requests = Some(n);
157 self
158 }
159
160 #[must_use]
163 pub const fn retire_after(mut self, age: Duration) -> Self {
164 self.max_age = Some(age);
165 self
166 }
167
168 #[must_use]
175 pub fn max_failures(mut self, n: u32) -> Self {
176 assert!(n > 0, "`Identities::max_failures` must be above zero");
177 self.max_failures = n;
178 self
179 }
180
181 #[must_use]
184 pub const fn rest(mut self, duration: Duration) -> Self {
185 self.rest = duration;
186 self
187 }
188}
189
190impl fmt::Debug for Identities {
191 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192 let source = match &self.source {
193 Source::Fixed(identities) => format!("{} listed", identities.len()),
194 Source::Generate(_) => "generated".to_owned(),
195 };
196 f.debug_struct("Identities")
197 .field("source", &source)
198 .field("max_active", &self.max_active)
199 .field("choice", &self.choice)
200 .field("max_requests", &self.max_requests)
201 .field("max_age", &self.max_age)
202 .field("max_failures", &self.max_failures)
203 .field("rest", &self.rest)
204 .finish()
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
213 #[should_panic(expected = "two identities are named `a`")]
214 fn listed_names_are_unique() {
215 let _ = Identities::new([Identity::new("a"), Identity::new("a")]);
216 }
217}