Skip to main content

ferrox_database_firebase/
lib.rs

1use async_trait::async_trait;
2use ferrox_database_core::Repository;
3use ferrox_errors::AppError;
4use reqwest::Client;
5use serde::{Serialize, de::DeserializeOwned};
6use std::marker::PhantomData;
7
8/// A Firebase Realtime Database/Firestore backed repository implementation.
9pub struct FirebaseRepository<T> {
10    client: Client,
11    base_url: String,
12    collection: String,
13    _marker: PhantomData<T>,
14}
15
16impl<T> FirebaseRepository<T> {
17    pub fn new(client: Client, project_id: &str, collection: &str) -> Self {
18        Self {
19            client,
20            base_url: format!("https://{}.firebaseio.com", project_id),
21            collection: collection.to_string(),
22            _marker: PhantomData,
23        }
24    }
25}
26
27#[async_trait]
28impl<T> Repository<T, String> for FirebaseRepository<T>
29where
30    T: Serialize + DeserializeOwned + Send + Sync + Clone,
31{
32    async fn find_by_id(&self, id: String) -> Result<Option<T>, AppError> {
33        let url = format!("{}/{}/{}.json", self.base_url, self.collection, id);
34        let res = self.client.get(&url).send().await.map_err(|e| AppError::DatabaseError(e.to_string()))?;
35        
36        if res.status().is_success() {
37            let entity: Option<T> = res.json().await.map_err(|e| AppError::DatabaseError(e.to_string()))?;
38            Ok(entity)
39        } else {
40            Ok(None)
41        }
42    }
43
44    async fn find_all(&self) -> Result<Vec<T>, AppError> {
45        let url = format!("{}/{}.json", self.base_url, self.collection);
46        let res = self.client.get(&url).send().await.map_err(|e| AppError::DatabaseError(e.to_string()))?;
47        
48        if res.status().is_success() {
49            let map: std::collections::HashMap<String, T> = res.json().await.map_err(|e| AppError::DatabaseError(e.to_string()))?;
50            Ok(map.into_values().collect())
51        } else {
52            Ok(vec![])
53        }
54    }
55
56    async fn insert(&self, entity: T) -> Result<T, AppError> {
57        let url = format!("{}/{}.json", self.base_url, self.collection);
58        
59        self.client.post(&url)
60            .json(&entity)
61            .send()
62            .await
63            .map_err(|e| AppError::DatabaseError(e.to_string()))?;
64            
65        Ok(entity)
66    }
67
68    async fn update(&self, id: String, entity: T) -> Result<T, AppError> {
69        let url = format!("{}/{}/{}.json", self.base_url, self.collection, id);
70        
71        self.client.put(&url)
72            .json(&entity)
73            .send()
74            .await
75            .map_err(|e| AppError::DatabaseError(e.to_string()))?;
76            
77        Ok(entity)
78    }
79
80    async fn delete(&self, id: String) -> Result<(), AppError> {
81        let url = format!("{}/{}/{}.json", self.base_url, self.collection, id);
82        self.client.delete(&url)
83            .send()
84            .await
85            .map_err(|e| AppError::DatabaseError(e.to_string()))?;
86            
87        Ok(())
88    }
89}