volga_oauth_client/
store.rs1use std::{
14 collections::HashMap,
15 sync::{Mutex, PoisonError},
16};
17
18use crate::TokenSet;
19
20pub trait TokenStore: Send + Sync {
22 fn get(&self, key: &str) -> Option<TokenSet>;
24
25 fn put(&self, key: &str, tokens: &TokenSet);
27
28 fn remove(&self, key: &str);
30}
31
32#[derive(Debug, Default)]
37pub struct InMemoryTokenStore {
38 entries: Mutex<HashMap<String, TokenSet>>,
39}
40
41impl InMemoryTokenStore {
42 #[inline]
44 pub fn new() -> Self {
45 Self::default()
46 }
47}
48
49impl TokenStore for InMemoryTokenStore {
50 fn get(&self, key: &str) -> Option<TokenSet> {
51 self.entries
52 .lock()
53 .unwrap_or_else(PoisonError::into_inner)
54 .get(key)
55 .cloned()
56 }
57
58 fn put(&self, key: &str, tokens: &TokenSet) {
59 self.entries
60 .lock()
61 .unwrap_or_else(PoisonError::into_inner)
62 .insert(key.to_owned(), tokens.clone());
63 }
64
65 fn remove(&self, key: &str) {
66 self.entries
67 .lock()
68 .unwrap_or_else(PoisonError::into_inner)
69 .remove(key);
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76
77 fn tokens(access_token: &str) -> TokenSet {
78 TokenSet {
79 access_token: access_token.into(),
80 token_type: "Bearer".into(),
81 refresh_token: None,
82 scope: None,
83 id_token: None,
84 expires_at: None,
85 }
86 }
87
88 #[test]
89 fn it_stores_replaces_and_removes_entries() {
90 let store = InMemoryTokenStore::new();
91 assert!(store.get("alice").is_none());
92
93 store.put("alice", &tokens("a1"));
94 store.put("bob", &tokens("b1"));
95 assert_eq!(store.get("alice").unwrap().access_token, "a1");
96
97 store.put("alice", &tokens("a2"));
98 assert_eq!(store.get("alice").unwrap().access_token, "a2");
99
100 store.remove("alice");
101 assert!(store.get("alice").is_none());
102 assert!(store.get("bob").is_some());
103 }
104}