firebase_rs_sdk/app/
types.rs

1use std::collections::HashMap;
2use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
3use std::sync::{Arc, Mutex};
4
5use crate::app::errors::{AppError, AppResult};
6use crate::component::constants::DEFAULT_ENTRY_NAME;
7use crate::component::types::DynService;
8use crate::component::{Component, ComponentContainer};
9
10#[allow(dead_code)]
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub struct VersionService {
13    pub library: String,
14    pub version: String,
15}
16
17#[allow(dead_code)]
18pub trait PlatformLoggerService: Send + Sync {
19    fn platform_info_string(&self) -> String;
20}
21
22pub trait HeartbeatService: Send + Sync {
23    fn trigger_heartbeat(&self) -> AppResult<()>;
24    #[allow(dead_code)]
25    fn heartbeats_header(&self) -> AppResult<Option<String>>;
26}
27
28pub trait HeartbeatStorage: Send + Sync {
29    fn read(&self) -> AppResult<HeartbeatsInStorage>;
30    fn overwrite(&self, value: &HeartbeatsInStorage) -> AppResult<()>;
31}
32
33#[derive(Clone, Debug, Default, PartialEq, Eq)]
34pub struct HeartbeatsInStorage {
35    pub last_sent_heartbeat_date: Option<String>,
36    pub heartbeats: Vec<SingleDateHeartbeat>,
37}
38
39#[derive(Clone, Debug, PartialEq, Eq)]
40pub struct SingleDateHeartbeat {
41    pub agent: String,
42    pub date: String,
43}
44
45#[derive(Clone, Debug, Default, PartialEq, Eq)]
46pub struct FirebaseOptions {
47    pub api_key: Option<String>,
48    pub auth_domain: Option<String>,
49    pub database_url: Option<String>,
50    pub project_id: Option<String>,
51    pub storage_bucket: Option<String>,
52    pub messaging_sender_id: Option<String>,
53    pub app_id: Option<String>,
54    pub measurement_id: Option<String>,
55}
56
57#[derive(Clone, Debug, PartialEq, Eq, Default)]
58pub struct FirebaseAppSettings {
59    pub name: Option<String>,
60    pub automatic_data_collection_enabled: Option<bool>,
61}
62
63#[derive(Clone, Debug, PartialEq, Eq)]
64pub struct FirebaseAppConfig {
65    pub name: Arc<str>,
66    pub automatic_data_collection_enabled: bool,
67}
68
69#[derive(Clone, Debug, PartialEq, Eq, Default)]
70pub struct FirebaseServerAppSettings {
71    pub automatic_data_collection_enabled: Option<bool>,
72    pub auth_id_token: Option<String>,
73    pub app_check_token: Option<String>,
74    pub release_on_deref: Option<bool>,
75}
76
77#[derive(Clone)]
78pub struct FirebaseApp {
79    inner: Arc<FirebaseAppInner>,
80}
81
82struct FirebaseAppInner {
83    options: FirebaseOptions,
84    config: FirebaseAppConfig,
85    automatic_data_collection_enabled: Mutex<bool>,
86    is_deleted: AtomicBool,
87    container: ComponentContainer,
88}
89
90impl FirebaseApp {
91    /// Creates a new `FirebaseApp` from options, config, and the component container.
92    pub fn new(
93        options: FirebaseOptions,
94        config: FirebaseAppConfig,
95        container: ComponentContainer,
96    ) -> Self {
97        let automatic = config.automatic_data_collection_enabled;
98        let inner = Arc::new(FirebaseAppInner {
99            options,
100            config,
101            automatic_data_collection_enabled: Mutex::new(automatic),
102            is_deleted: AtomicBool::new(false),
103            container,
104        });
105        let app = Self {
106            inner: inner.clone(),
107        };
108        let dyn_service: DynService = Arc::new(app.clone());
109        app.inner.container.attach_root_service(dyn_service);
110        app
111    }
112
113    /// Returns the app's logical name.
114    pub fn name(&self) -> &str {
115        &self.inner.config.name
116    }
117
118    /// Provides a cloned copy of the original Firebase options.
119    pub fn options(&self) -> FirebaseOptions {
120        self.inner.options.clone()
121    }
122
123    /// Returns the configuration metadata associated with the app.
124    pub fn config(&self) -> FirebaseAppConfig {
125        self.inner.config.clone()
126    }
127
128    /// Indicates whether automatic data collection is currently enabled.
129    pub fn automatic_data_collection_enabled(&self) -> bool {
130        *self
131            .inner
132            .automatic_data_collection_enabled
133            .lock()
134            .unwrap_or_else(|poison| poison.into_inner())
135    }
136
137    /// Updates the automatic data collection flag for the app.
138    pub fn set_automatic_data_collection_enabled(&self, value: bool) {
139        *self
140            .inner
141            .automatic_data_collection_enabled
142            .lock()
143            .unwrap_or_else(|poison| poison.into_inner()) = value;
144    }
145
146    /// Exposes the component container for advanced service registration.
147    pub fn container(&self) -> ComponentContainer {
148        self.inner.container.clone()
149    }
150
151    /// Adds a lazily-initialized component to the app.
152    pub fn add_component(&self, component: Component) -> AppResult<()> {
153        self.check_destroyed()?;
154        self.inner
155            .container
156            .add_component(component)
157            .map_err(AppError::from)
158    }
159
160    /// Adds a component, replacing any existing implementation with the same name.
161    pub fn add_or_overwrite_component(&self, component: Component) -> AppResult<()> {
162        self.check_destroyed()?;
163        self.inner.container.add_or_overwrite_component(component);
164        Ok(())
165    }
166
167    /// Removes a cached service instance from the specified provider.
168    pub fn remove_service_instance(&self, name: &str, identifier: Option<&str>) {
169        let provider = self.inner.container.get_provider(name);
170        if let Some(id) = identifier {
171            provider.clear_instance(id);
172        } else {
173            provider.clear_instance(DEFAULT_ENTRY_NAME);
174        }
175    }
176
177    /// Returns whether the app has been explicitly deleted.
178    pub fn is_deleted(&self) -> bool {
179        self.inner.is_deleted.load(Ordering::SeqCst)
180    }
181
182    /// Marks the app as deleted (internal use).
183    pub fn set_is_deleted(&self, value: bool) {
184        self.inner.is_deleted.store(value, Ordering::SeqCst);
185    }
186
187    /// Verifies that the app has not been deleted before performing operations.
188    pub fn check_destroyed(&self) -> AppResult<()> {
189        if self.is_deleted() {
190            return Err(AppError::AppDeleted {
191                app_name: self.name().to_owned(),
192            });
193        }
194        Ok(())
195    }
196}
197
198impl std::fmt::Debug for FirebaseApp {
199    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
200        f.debug_struct("FirebaseApp")
201            .field("name", &self.name())
202            .field(
203                "automatic_data_collection_enabled",
204                &self.automatic_data_collection_enabled(),
205            )
206            .finish()
207    }
208}
209
210impl FirebaseAppConfig {
211    /// Creates a configuration value capturing the app name and data collection setting.
212    pub fn new(name: impl Into<String>, automatic: bool) -> Self {
213        Self {
214            name: to_arc_str(name),
215            automatic_data_collection_enabled: automatic,
216        }
217    }
218}
219
220#[derive(Clone)]
221pub struct FirebaseServerApp {
222    inner: Arc<FirebaseServerAppInner>,
223}
224
225struct FirebaseServerAppInner {
226    base: FirebaseApp,
227    settings: FirebaseServerAppSettings,
228    ref_count: AtomicUsize,
229}
230
231impl FirebaseServerApp {
232    /// Wraps a `FirebaseApp` with server-specific settings and reference counting.
233    pub fn new(base: FirebaseApp, settings: FirebaseServerAppSettings) -> Self {
234        Self {
235            inner: Arc::new(FirebaseServerAppInner {
236                base,
237                settings,
238                ref_count: AtomicUsize::new(1),
239            }),
240        }
241    }
242
243    /// Returns the underlying base app instance.
244    pub fn base(&self) -> &FirebaseApp {
245        &self.inner.base
246    }
247
248    /// Returns the server-specific configuration for this app.
249    pub fn settings(&self) -> FirebaseServerAppSettings {
250        self.inner.settings.clone()
251    }
252
253    /// Convenience accessor for the app name.
254    pub fn name(&self) -> &str {
255        self.inner.base.name()
256    }
257
258    /// Increments the manual reference count.
259    pub fn inc_ref_count(&self) {
260        self.inner.ref_count.fetch_add(1, Ordering::SeqCst);
261    }
262
263    /// Decrements the reference count, returning the new value.
264    pub fn dec_ref_count(&self) -> usize {
265        self.inner.ref_count.fetch_sub(1, Ordering::SeqCst) - 1
266    }
267}
268
269#[allow(dead_code)]
270/// Returns `true` when the current target behaves like a browser environment.
271pub fn is_browser() -> bool {
272    false
273}
274
275#[allow(dead_code)]
276/// Returns `true` when the current target is a web worker environment.
277pub fn is_web_worker() -> bool {
278    false
279}
280
281/// Provides compile-time default app options when available.
282pub fn get_default_app_config() -> Option<FirebaseOptions> {
283    None
284}
285
286/// Compares two `FirebaseOptions` instances for structural equality.
287pub fn deep_equal_options(a: &FirebaseOptions, b: &FirebaseOptions) -> bool {
288    a == b
289}
290
291#[allow(dead_code)]
292#[derive(Clone, Debug)]
293pub struct FirebaseAuthTokenData {
294    pub access_token: String,
295}
296
297#[allow(dead_code)]
298pub trait FirebaseServiceInternals: Send + Sync {
299    fn delete(&self) -> AppResult<()>;
300}
301
302#[allow(dead_code)]
303pub trait FirebaseService: Send + Sync {
304    fn app(&self) -> FirebaseApp;
305    fn internals(&self) -> Option<&dyn FirebaseServiceInternals> {
306        None
307    }
308}
309
310#[allow(dead_code)]
311pub type AppHook = Arc<dyn Fn(&str, &FirebaseApp) + Send + Sync>;
312
313#[allow(dead_code)]
314pub type FirebaseServiceFactory<T> = Arc<
315    dyn Fn(
316            &FirebaseApp,
317            Option<Arc<dyn Fn(&HashMap<String, serde_json::Value>) + Send + Sync>>,
318            Option<&str>,
319        ) -> T
320        + Send
321        + Sync,
322>;
323
324#[allow(dead_code)]
325pub type FirebaseServiceNamespace<T> = Arc<dyn Fn(Option<&FirebaseApp>) -> T + Send + Sync>;
326
327#[allow(dead_code)]
328pub trait FirebaseAppInternals: Send + Sync {
329    fn get_token(&self, refresh_token: bool) -> AppResult<Option<FirebaseAuthTokenData>>;
330    fn get_uid(&self) -> Option<String>;
331    fn add_auth_token_listener(&self, listener: Arc<dyn Fn(Option<String>) + Send + Sync>);
332    fn remove_auth_token_listener(&self, listener_id: usize);
333    fn log_event(
334        &self,
335        event_name: &str,
336        event_params: HashMap<String, serde_json::Value>,
337        global: bool,
338    );
339}
340
341/// Compares two app configs for equality.
342pub fn deep_equal_config(a: &FirebaseAppConfig, b: &FirebaseAppConfig) -> bool {
343    a == b
344}
345
346fn to_arc_str(value: impl Into<String>) -> Arc<str> {
347    Arc::from(value.into().into_boxed_str())
348}