rusty_oss/credentials/
rotating.rsuse std::fmt::{Debug, Formatter, Result as FmtResult};
use std::sync::{Arc, RwLock};
use super::Credentials;
#[allow(clippy::module_name_repetitions)]
pub struct RotatingCredentials {
inner: Arc<RwLock<Arc<Credentials>>>,
}
impl RotatingCredentials {
#[must_use]
pub fn new(key: String, secret: String, token: Option<String>) -> Self {
let credentials = Credentials::new_with_maybe_token(key, secret, token);
Self {
inner: Arc::new(RwLock::new(Arc::new(credentials))),
}
}
#[must_use]
pub fn get(&self) -> Arc<Credentials> {
let lock = self.inner.read().expect("can't be poisoned");
Arc::clone(&lock)
}
pub fn update(&self, key: String, secret: String, token: Option<String>) {
let credentials = Credentials::new_with_maybe_token(key, secret, token);
let mut lock = self.inner.write().expect("can't be poisoned");
match Arc::get_mut(&mut lock) {
Some(arc) => *arc = credentials,
None => *lock = Arc::new(credentials),
};
}
}
impl Debug for RotatingCredentials {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
let current = self.get();
Debug::fmt(&*current, f)
}
}
impl Clone for RotatingCredentials {
fn clone(&self) -> Self {
Self {
inner: Arc::clone(&self.inner),
}
}
fn clone_from(&mut self, source: &Self) {
self.inner = Arc::clone(&source.inner);
}
}
impl PartialEq for RotatingCredentials {
fn eq(&self, other: &Self) -> bool {
let current1 = self.get();
let current2 = other.get();
*current1 == *current2
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn rotate() {
let credentials =
RotatingCredentials::new("abcd".into(), "1234".into(), Some("xyz".into()));
let current = credentials.get();
assert_eq!(current.key(), "abcd");
assert_eq!(current.secret(), "1234");
assert_eq!(current.token(), Some("xyz"));
drop(current);
credentials.update("1234".into(), "5678".into(), Some("9012".into()));
let current = credentials.get();
assert_eq!(current.key(), "1234");
assert_eq!(current.secret(), "5678");
assert_eq!(current.token(), Some("9012"));
drop(current);
credentials.update("dcba".into(), "4321".into(), Some("yxz".into()));
let current = credentials.get();
assert_eq!(current.key(), "dcba");
assert_eq!(current.secret(), "4321");
assert_eq!(current.token(), Some("yxz"));
drop(current);
}
#[test]
fn rotate_cloned() {
let credentials =
RotatingCredentials::new("abcd".into(), "1234".into(), Some("xyz".into()));
let current = credentials.get();
assert_eq!(current.key(), "abcd");
assert_eq!(current.secret(), "1234");
assert_eq!(current.token(), Some("xyz"));
drop(current);
let credentials2 = credentials.clone();
credentials.update("1234".into(), "5678".into(), Some("9012".into()));
let current = credentials2.get();
assert_eq!(current.key(), "1234");
assert_eq!(current.secret(), "5678");
assert_eq!(current.token(), Some("9012"));
drop(current);
assert_eq!(credentials, credentials2);
credentials.update("dcba".into(), "4321".into(), Some("yxz".into()));
let current = credentials.get();
assert_eq!(current.key(), "dcba");
assert_eq!(current.secret(), "4321");
assert_eq!(current.token(), Some("yxz"));
drop(current);
assert_eq!(credentials, credentials2);
}
#[test]
fn debug() {
let credentials =
RotatingCredentials::new("abcd".into(), "1234".into(), Some("xyz".into()));
let debug_output = format!("{credentials:?}");
assert_eq!(debug_output, "Credentials { key: \"abcd\", .. }");
}
}