rusty_s3/credentials/
rotating.rs1use std::fmt::{Debug, Formatter, Result as FmtResult};
2use std::sync::{Arc, RwLock};
3
4use super::Credentials;
5
6#[allow(clippy::module_name_repetitions)]
14pub struct RotatingCredentials {
15 inner: Arc<RwLock<Arc<Credentials>>>,
16}
17
18impl RotatingCredentials {
19 #[must_use]
21 pub fn new(key: String, secret: String, token: Option<String>) -> Self {
22 let credentials = Credentials::new_with_maybe_token(key, secret, token);
23
24 Self {
25 inner: Arc::new(RwLock::new(Arc::new(credentials))),
26 }
27 }
28
29 #[must_use]
35 pub fn get(&self) -> Arc<Credentials> {
36 let lock = self.inner.read().expect("can't be poisoned");
37 Arc::clone(&lock)
38 }
39
40 pub fn update(&self, key: String, secret: String, token: Option<String>) {
46 let credentials = Credentials::new_with_maybe_token(key, secret, token);
47
48 let mut lock = self.inner.write().expect("can't be poisoned");
49 match Arc::get_mut(&mut lock) {
50 Some(arc) => *arc = credentials,
51 None => *lock = Arc::new(credentials),
52 };
53 }
54}
55
56impl Debug for RotatingCredentials {
57 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
58 let current = self.get();
59 Debug::fmt(&*current, f)
60 }
61}
62
63impl Clone for RotatingCredentials {
64 fn clone(&self) -> Self {
65 Self {
66 inner: Arc::clone(&self.inner),
67 }
68 }
69
70 fn clone_from(&mut self, source: &Self) {
71 self.inner = Arc::clone(&source.inner);
72 }
73}
74
75impl PartialEq for RotatingCredentials {
76 fn eq(&self, other: &Self) -> bool {
77 let current1 = self.get();
78 let current2 = other.get();
79 *current1 == *current2
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use pretty_assertions::assert_eq;
86
87 use super::*;
88
89 #[test]
90 fn rotate() {
91 let credentials =
92 RotatingCredentials::new("abcd".into(), "1234".into(), Some("xyz".into()));
93
94 let current = credentials.get();
95 assert_eq!(current.key(), "abcd");
96 assert_eq!(current.secret(), "1234");
97 assert_eq!(current.token(), Some("xyz"));
98 drop(current);
99
100 credentials.update("1234".into(), "5678".into(), Some("9012".into()));
101
102 let current = credentials.get();
103 assert_eq!(current.key(), "1234");
104 assert_eq!(current.secret(), "5678");
105 assert_eq!(current.token(), Some("9012"));
106 drop(current);
107
108 credentials.update("dcba".into(), "4321".into(), Some("yxz".into()));
109
110 let current = credentials.get();
111 assert_eq!(current.key(), "dcba");
112 assert_eq!(current.secret(), "4321");
113 assert_eq!(current.token(), Some("yxz"));
114 drop(current);
115 }
116
117 #[test]
118 fn rotate_cloned() {
119 let credentials =
120 RotatingCredentials::new("abcd".into(), "1234".into(), Some("xyz".into()));
121
122 let current = credentials.get();
123 assert_eq!(current.key(), "abcd");
124 assert_eq!(current.secret(), "1234");
125 assert_eq!(current.token(), Some("xyz"));
126 drop(current);
127
128 let credentials2 = credentials.clone();
129
130 credentials.update("1234".into(), "5678".into(), Some("9012".into()));
131
132 let current = credentials2.get();
133 assert_eq!(current.key(), "1234");
134 assert_eq!(current.secret(), "5678");
135 assert_eq!(current.token(), Some("9012"));
136 drop(current);
137
138 assert_eq!(credentials, credentials2);
139
140 credentials.update("dcba".into(), "4321".into(), Some("yxz".into()));
141
142 let current = credentials.get();
143 assert_eq!(current.key(), "dcba");
144 assert_eq!(current.secret(), "4321");
145 assert_eq!(current.token(), Some("yxz"));
146 drop(current);
147
148 assert_eq!(credentials, credentials2);
149 }
150
151 #[test]
152 fn debug() {
153 let credentials =
154 RotatingCredentials::new("abcd".into(), "1234".into(), Some("xyz".into()));
155 let debug_output = format!("{credentials:?}");
156 assert_eq!(debug_output, "Credentials { key: \"abcd\", .. }");
157 }
158}