use firestore::{FirestoreDb, FirestoreQueryParams, FirestoreDbOptions, FirestoreQueryCollection};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use gcloud_sdk::TokenSourceType;
use std::path::PathBuf;
type Error = Box<dyn std::error::Error + Send + Sync>;
async fn get_fs_db(cfg: &CLConfig) -> Result<FirestoreDb, Error> {
Ok(FirestoreDb::with_options_token_source(
FirestoreDbOptions::new(cfg.project_id.clone(),),
gcloud_sdk::GCP_DEFAULT_SCOPES.clone(),
TokenSourceType::File(PathBuf::from(&cfg.cred_path)),
).await?)
}
#[async_trait]
pub trait CloudSync<T> where
for<'a> Self: Deserialize<'a> + Serialize + Unique<T> + Sync + Send,
T: Serialize + std::fmt::Display + std::cmp::Eq + std::hash::Hash + Send + Sync {
async fn save(&self) -> Result<(), Error> {
let cfg = Self::config();
let db = get_fs_db(&cfg).await?;
db.delete_by_id(&cfg.collection, self.uuid().to_string()).await?;
db.create_obj(&cfg.collection, self.uuid().to_string(), self).await?;
Ok(())
}
async fn rm(&self) -> Result<(), Error> {
let cfg = Self::config();
let db = get_fs_db(&cfg).await?;
db.delete_by_id(&cfg.collection, self.uuid().to_string()).await?;
Ok(())
}
async fn get() -> Result<Vec<Self>, Error> {
let cfg = Self::config();
let db = get_fs_db(&cfg).await?;
let objects: Vec<Self> = db.query_obj(FirestoreQueryParams::new(FirestoreQueryCollection::Single(cfg.collection))).await?;
Ok(objects)
}
async fn hash() -> Result<HashMap<T, Self>, Error> {
let cfg = Self::config();
let db = get_fs_db(&cfg).await?;
let objects: Vec<Self> = db.query_obj(FirestoreQueryParams::new(FirestoreQueryCollection::Single(cfg.collection))).await?;
let mut hash = HashMap::new();
for obj in objects {
hash.insert(obj.uuid(), obj);
}
Ok(hash)
}
fn config() -> CLConfig;
}
pub trait Unique<T> where T: Serialize {
fn uuid(&self) -> T;
}
pub struct CLConfig {
pub project_id: String,
pub cred_path: String,
pub collection: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Deserialize, Serialize)]
struct TestOBJ {
key: String,
data: String,
}
impl CloudSync<String> for TestOBJ {
fn config() -> CLConfig {
CLConfig {
project_id: "cloudsync-testing".to_string(),
cred_path: "./firebase.json".to_string(),
collection: "testing".to_string(),
}
}
}
impl Unique<String> for TestOBJ {
fn uuid(&self) -> String {
return String::from(&self.key);
}
}
#[tokio::test]
async fn testSavingObject() {
let obj = TestOBJ {
key: "aaa".to_string(),
data: "data".to_string(),
};
obj.save().await;
let vec = TestOBJ::get().await.unwrap();
assert_eq!(vec.len(), 1);
}
}