firebase_rs_sdk/auth/persistence/
mod.rs1use std::sync::atomic::{AtomicUsize, Ordering};
2use std::sync::{Arc, Mutex};
3
4use serde::{Deserialize, Serialize};
5
6use crate::auth::error::AuthResult;
7
8#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
9pub struct PersistedAuthState {
10 pub user_id: String,
11 pub email: Option<String>,
12 pub refresh_token: Option<String>,
13 pub access_token: Option<String>,
14 pub expires_at: Option<i64>,
16}
17
18pub type PersistenceListener = Arc<dyn Fn(Option<PersistedAuthState>) + Send + Sync>;
19
20#[derive(Default)]
21struct InMemoryState {
22 value: Option<PersistedAuthState>,
23 listeners: Vec<(usize, PersistenceListener)>,
24}
25
26pub struct PersistenceSubscription {
27 cleanup: Option<Box<dyn FnOnce() + Send + 'static>>,
28}
29
30impl PersistenceSubscription {
31 pub fn new<F>(cleanup: F) -> Self
33 where
34 F: FnOnce() + Send + 'static,
35 {
36 Self {
37 cleanup: Some(Box::new(cleanup)),
38 }
39 }
40
41 pub fn noop() -> Self {
43 Self { cleanup: None }
44 }
45}
46
47impl Default for PersistenceSubscription {
48 fn default() -> Self {
49 Self::noop()
50 }
51}
52
53impl Drop for PersistenceSubscription {
54 fn drop(&mut self) {
55 if let Some(cleanup) = self.cleanup.take() {
56 cleanup();
57 }
58 }
59}
60
61pub trait AuthPersistence: Send + Sync {
68 fn set(&self, state: Option<PersistedAuthState>) -> AuthResult<()>;
69 fn get(&self) -> AuthResult<Option<PersistedAuthState>>;
70
71 fn subscribe(&self, _listener: PersistenceListener) -> AuthResult<PersistenceSubscription> {
72 Ok(PersistenceSubscription::noop())
73 }
74}
75
76pub struct InMemoryPersistence {
77 state: Arc<Mutex<InMemoryState>>,
78 next_id: AtomicUsize,
79}
80
81impl Default for InMemoryPersistence {
82 fn default() -> Self {
83 Self {
84 state: Arc::new(Mutex::new(InMemoryState::default())),
85 next_id: AtomicUsize::new(1),
86 }
87 }
88}
89
90impl AuthPersistence for InMemoryPersistence {
91 fn set(&self, state: Option<PersistedAuthState>) -> AuthResult<()> {
92 let listeners = {
93 let mut guard = self.state.lock().unwrap();
94 guard.value = state.clone();
95 guard
96 .listeners
97 .iter()
98 .map(|(_, listener)| listener.clone())
99 .collect::<Vec<_>>()
100 };
101
102 for listener in listeners {
103 listener(state.clone());
104 }
105
106 Ok(())
107 }
108
109 fn get(&self) -> AuthResult<Option<PersistedAuthState>> {
110 Ok(self.state.lock().unwrap().value.clone())
111 }
112
113 fn subscribe(&self, listener: PersistenceListener) -> AuthResult<PersistenceSubscription> {
114 let id = self.next_id.fetch_add(1, Ordering::SeqCst);
115 {
116 let mut guard = self.state.lock().unwrap();
117 guard.listeners.push((id, listener));
118 }
119
120 let state = Arc::downgrade(&self.state);
121 Ok(PersistenceSubscription::new(move || {
122 if let Some(state) = state.upgrade() {
123 if let Ok(mut guard) = state.lock() {
124 guard
125 .listeners
126 .retain(|(listener_id, _)| *listener_id != id);
127 }
128 }
129 }))
130 }
131}
132
133type DynSetFn = dyn Fn(Option<PersistedAuthState>) -> AuthResult<()> + Send + Sync;
134type DynGetFn = dyn Fn() -> AuthResult<Option<PersistedAuthState>> + Send + Sync;
135type DynSubscribeFn =
136 dyn Fn(PersistenceListener) -> AuthResult<PersistenceSubscription> + Send + Sync;
137
138pub struct ClosurePersistence {
139 set_fn: Arc<DynSetFn>,
140 get_fn: Arc<DynGetFn>,
141 subscribe_fn: Arc<DynSubscribeFn>,
142}
143
144impl ClosurePersistence {
145 pub fn new<Set, Get>(set: Set, get: Get) -> Self
147 where
148 Set: Fn(Option<PersistedAuthState>) -> AuthResult<()> + Send + Sync + 'static,
149 Get: Fn() -> AuthResult<Option<PersistedAuthState>> + Send + Sync + 'static,
150 {
151 Self::with_subscribe(set, get, |_| Ok(PersistenceSubscription::noop()))
152 }
153
154 pub fn with_subscribe<Set, Get, Subscribe>(set: Set, get: Get, subscribe: Subscribe) -> Self
156 where
157 Set: Fn(Option<PersistedAuthState>) -> AuthResult<()> + Send + Sync + 'static,
158 Get: Fn() -> AuthResult<Option<PersistedAuthState>> + Send + Sync + 'static,
159 Subscribe:
160 Fn(PersistenceListener) -> AuthResult<PersistenceSubscription> + Send + Sync + 'static,
161 {
162 Self {
163 set_fn: Arc::new(set),
164 get_fn: Arc::new(get),
165 subscribe_fn: Arc::new(subscribe),
166 }
167 }
168}
169
170#[cfg(all(
171 feature = "wasm-web",
172 target_arch = "wasm32",
173 feature = "experimental-indexed-db"
174))]
175pub mod indexed_db;
176
177#[cfg(not(all(feature = "wasm-web", target_arch = "wasm32")))]
178pub mod file;
179
180impl AuthPersistence for ClosurePersistence {
181 fn set(&self, state: Option<PersistedAuthState>) -> AuthResult<()> {
182 (self.set_fn)(state)
183 }
184
185 fn get(&self) -> AuthResult<Option<PersistedAuthState>> {
186 (self.get_fn)()
187 }
188
189 fn subscribe(&self, listener: PersistenceListener) -> AuthResult<PersistenceSubscription> {
190 (self.subscribe_fn)(listener)
191 }
192}
193
194#[cfg(all(target_arch = "wasm32", feature = "wasm-web"))]
195pub mod web;