openmoat_proxy/broker/
value.rs1use std::collections::VecDeque;
14use std::sync::{Arc, PoisonError, RwLock, RwLockReadGuard};
15use std::time::{Duration, Instant, SystemTime};
16
17use zeroize::Zeroizing;
18
19use super::BrokerError;
20
21const KEPT: usize = 4;
23
24pub(super) struct Slot {
26 current: Zeroizing<String>,
27 until: Option<(Instant, SystemTime)>,
28 replaced: VecDeque<Zeroizing<String>>,
29}
30
31impl Slot {
32 pub(super) fn fixed(value: Zeroizing<String>) -> Arc<RwLock<Self>> {
33 Arc::new(RwLock::new(Self {
34 current: value,
35 until: None,
36 replaced: VecDeque::new(),
37 }))
38 }
39
40 pub(super) fn live(&self) -> Option<&Zeroizing<String>> {
42 match self.until {
43 Some((mono, wall)) if Instant::now() >= mono || SystemTime::now() >= wall => None,
44 _ => Some(&self.current),
45 }
46 }
47
48 pub(super) fn known(&self) -> impl Iterator<Item = &Zeroizing<String>> {
51 std::iter::once(&self.current).chain(&self.replaced)
52 }
53}
54
55pub(super) fn read(slot: &RwLock<Slot>) -> RwLockReadGuard<'_, Slot> {
57 slot.read().unwrap_or_else(PoisonError::into_inner)
58}
59
60#[derive(Clone)]
63pub struct Rotation {
64 id: String,
65 slot: Arc<RwLock<Slot>>,
66}
67
68impl std::fmt::Debug for Rotation {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 f.debug_struct("Rotation")
71 .field("id", &self.id)
72 .finish_non_exhaustive()
73 }
74}
75
76impl Rotation {
77 pub(super) fn new(id: &str, slot: &Arc<RwLock<Slot>>) -> Self {
78 Self {
79 id: id.to_owned(),
80 slot: Arc::clone(slot),
81 }
82 }
83
84 pub fn renew(&self, value: Zeroizing<String>, lifetime: Duration) -> Result<(), BrokerError> {
88 super::usable(&self.id, &value)?;
89 let until = (
90 Instant::now() + lifetime,
91 SystemTime::now()
92 .checked_add(lifetime)
93 .unwrap_or(SystemTime::UNIX_EPOCH),
94 );
95 let mut slot = self.slot.write().unwrap_or_else(PoisonError::into_inner);
96 if *slot.current != *value {
97 let old = std::mem::replace(&mut slot.current, value);
98 slot.replaced.push_front(old);
99 slot.replaced.truncate(KEPT);
100 }
101 slot.until = Some(until);
102 Ok(())
103 }
104
105 #[must_use]
108 pub fn remaining(&self) -> Option<Duration> {
109 let slot = read(&self.slot);
110 let (mono, wall) = slot.until?;
111 let by_wall = wall
112 .duration_since(SystemTime::now())
113 .unwrap_or(Duration::ZERO);
114 Some(mono.saturating_duration_since(Instant::now()).min(by_wall))
115 }
116}
117
118#[cfg(test)]
119mod tests;