use std::sync::{Arc, RwLock};
#[derive(Clone, PartialEq, Eq)]
pub struct RuntimeLeaseV1(Vec<u8>);
impl RuntimeLeaseV1 {
pub fn from_bytes(bytes: Vec<u8>) -> Self {
Self(bytes)
}
pub(crate) fn as_bytes(&self) -> &[u8] {
&self.0
}
}
impl std::fmt::Debug for RuntimeLeaseV1 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "RuntimeLeaseV1({} bytes)", self.0.len())
}
}
pub trait LeaseSource: Send + Sync {
fn current(&self) -> Option<RuntimeLeaseV1>;
}
static SOURCE: RwLock<Option<Arc<dyn LeaseSource>>> = RwLock::new(None);
pub fn set_lease_source(source: Box<dyn LeaseSource>) {
let source: Arc<dyn LeaseSource> = Arc::from(source);
*SOURCE
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = Some(source);
}
pub(crate) fn current_lease() -> Option<RuntimeLeaseV1> {
let source = SOURCE
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.clone()?;
source.current()
}
pub(crate) fn current_lease_b64() -> Option<String> {
use base64::Engine as _;
let lease = current_lease()?;
Some(base64::engine::general_purpose::STANDARD.encode(lease.as_bytes()))
}
#[cfg(test)]
mod tests {
use super::*;
struct Fixed(Option<Vec<u8>>);
impl LeaseSource for Fixed {
fn current(&self) -> Option<RuntimeLeaseV1> {
self.0.clone().map(RuntimeLeaseV1::from_bytes)
}
}
struct Reentrant;
impl LeaseSource for Reentrant {
fn current(&self) -> Option<RuntimeLeaseV1> {
set_lease_source(Box::new(Fixed(Some(b"after".to_vec()))));
Some(RuntimeLeaseV1::from_bytes(b"during".to_vec()))
}
}
#[test]
fn the_registered_source_decides_and_a_later_one_replaces_it() {
set_lease_source(Box::new(Fixed(None)));
assert_eq!(current_lease(), None);
set_lease_source(Box::new(Fixed(Some(b"lease-1".to_vec()))));
let lease = current_lease().expect("registered lease");
assert_eq!(lease.as_bytes(), b"lease-1");
assert_eq!(format!("{lease:?}"), "RuntimeLeaseV1(7 bytes)");
set_lease_source(Box::new(Reentrant));
assert_eq!(current_lease().expect("lease").as_bytes(), b"during");
assert_eq!(current_lease().expect("lease").as_bytes(), b"after");
}
}