firestore 0.49.0

Library provides a simple API for Google Firestore and own Serde serializer based on efficient gRPC API
Documentation
use crate::db::safe_document_path;
use crate::{
    FirestoreDb, FirestoreError, FirestoreFieldTransform, FirestoreResult,
    FirestoreWritePrecondition,
};
use gcloud_sdk::google::firestore::v1::Write;
use serde::Serialize;

#[derive(Debug, PartialEq, Clone)]
pub(crate) struct UpdateObjectOperation<'a, T, S>
where
    T: Serialize + Sync + Send,
    S: AsRef<str>,
{
    pub parent: String,
    pub collection_id: String,
    pub document_id: S,
    pub obj: &'a T,
    pub update_only: Option<Vec<String>>,
    pub precondition: Option<FirestoreWritePrecondition>,
    pub update_transforms: Vec<FirestoreFieldTransform>,
}

impl<'a, T, S> TryInto<Write> for UpdateObjectOperation<'a, T, S>
where
    T: Serialize + Sync + Send,
    S: AsRef<str>,
{
    type Error = FirestoreError;

    fn try_into(self) -> Result<Write, Self::Error> {
        Ok(Write {
            update_mask: self.update_only.map({
                |vf| gcloud_sdk::google::firestore::v1::DocumentMask {
                    field_paths: vf.iter().map(|f| f.to_string()).collect(),
                }
            }),
            update_transforms: self
                .update_transforms
                .into_iter()
                .map(|s| s.try_into())
                .collect::<FirestoreResult<
                Vec<gcloud_sdk::google::firestore::v1::document_transform::FieldTransform>,
            >>()?,
            current_document: self.precondition.map(|cond| cond.try_into()).transpose()?,
            operation: Some(gcloud_sdk::google::firestore::v1::write::Operation::Update(
                FirestoreDb::serialize_to_doc(
                    safe_document_path(
                        &self.parent,
                        self.collection_id.as_str(),
                        self.document_id.as_ref(),
                    )?,
                    &self.obj,
                )?,
            )),
        })
    }
}

#[derive(Debug, Eq, PartialEq, Clone)]
pub(crate) struct DeleteOperation<S>
where
    S: AsRef<str>,
{
    pub parent: String,
    pub collection_id: String,
    pub document_id: S,
    pub precondition: Option<FirestoreWritePrecondition>,
}

impl<S> TryInto<Write> for DeleteOperation<S>
where
    S: AsRef<str>,
{
    type Error = FirestoreError;

    fn try_into(self) -> Result<Write, Self::Error> {
        Ok(Write {
            update_mask: None,
            update_transforms: vec![],
            current_document: self.precondition.map(|cond| cond.try_into()).transpose()?,
            operation: Some(gcloud_sdk::google::firestore::v1::write::Operation::Delete(
                safe_document_path(
                    &self.parent,
                    self.collection_id.as_str(),
                    self.document_id.as_ref(),
                )?,
            )),
        })
    }
}

#[derive(Debug, PartialEq, Clone)]
pub(crate) struct TransformObjectOperation<S>
where
    S: AsRef<str>,
{
    pub parent: String,
    pub collection_id: String,
    pub document_id: S,
    pub precondition: Option<FirestoreWritePrecondition>,
    pub transforms: Vec<FirestoreFieldTransform>,
}

impl<S> TryInto<Write> for TransformObjectOperation<S>
where
    S: AsRef<str>,
{
    type Error = FirestoreError;

    fn try_into(self) -> Result<Write, Self::Error> {
        Ok(Write {
            update_mask: None,
            update_transforms: vec![],
            current_document: self.precondition.map(|cond| cond.try_into()).transpose()?,
            operation: Some(gcloud_sdk::google::firestore::v1::write::Operation::Transform(
                gcloud_sdk::google::firestore::v1::DocumentTransform {
                    document: safe_document_path(
                        &self.parent,
                        self.collection_id.as_str(),
                        self.document_id.as_ref(),
                    )?,
                    field_transforms: self.transforms
                        .into_iter()
                        .map(|s| s.try_into())
                        .collect::<FirestoreResult<Vec<gcloud_sdk::google::firestore::v1::document_transform::FieldTransform>>>()?
                }
            )),
        })
    }
}

pub trait FirestoreTransactionOps {
    fn add<I>(&mut self, write: I) -> FirestoreResult<&mut Self>
    where
        I: TryInto<gcloud_sdk::google::firestore::v1::Write, Error = FirestoreError>;

    fn get_documents_path(&self) -> &String;

    fn update_object<T, S>(
        &mut self,
        collection_id: &str,
        document_id: S,
        obj: &T,
        update_only: Option<Vec<String>>,
        precondition: Option<FirestoreWritePrecondition>,
        update_transforms: Vec<FirestoreFieldTransform>,
    ) -> FirestoreResult<&mut Self>
    where
        T: Serialize + Sync + Send,
        S: AsRef<str>,
    {
        self.update_object_at(
            &self.get_documents_path().clone(),
            collection_id,
            document_id,
            obj,
            update_only,
            precondition,
            update_transforms,
        )
    }

    fn update_object_at<T, S>(
        &mut self,
        parent: &str,
        collection_id: &str,
        document_id: S,
        obj: &T,
        update_only: Option<Vec<String>>,
        precondition: Option<FirestoreWritePrecondition>,
        update_transforms: Vec<FirestoreFieldTransform>,
    ) -> FirestoreResult<&mut Self>
    where
        T: Serialize + Sync + Send,
        S: AsRef<str>,
    {
        self.add(UpdateObjectOperation {
            parent: parent.to_string(),
            collection_id: collection_id.to_string(),
            document_id,
            obj,
            update_only,
            precondition,
            update_transforms,
        })
    }

    fn delete_by_id<S>(
        &mut self,
        collection_id: &str,
        document_id: S,
        precondition: Option<FirestoreWritePrecondition>,
    ) -> FirestoreResult<&mut Self>
    where
        S: AsRef<str>,
    {
        self.delete_by_id_at(
            &self.get_documents_path().clone(),
            collection_id,
            document_id,
            precondition,
        )
    }

    fn delete_by_id_at<S>(
        &mut self,
        parent: &str,
        collection_id: &str,
        document_id: S,
        precondition: Option<FirestoreWritePrecondition>,
    ) -> FirestoreResult<&mut Self>
    where
        S: AsRef<str>,
    {
        self.add(DeleteOperation {
            parent: parent.to_string(),
            collection_id: collection_id.to_string(),
            document_id,
            precondition,
        })
    }

    fn transform<S>(
        &mut self,
        collection_id: &str,
        document_id: S,
        precondition: Option<FirestoreWritePrecondition>,
        transforms: Vec<FirestoreFieldTransform>,
    ) -> FirestoreResult<&mut Self>
    where
        S: AsRef<str>,
    {
        self.transform_at(
            &self.get_documents_path().clone(),
            collection_id,
            document_id,
            precondition,
            transforms,
        )
    }

    fn transform_at<S>(
        &mut self,
        parent: &str,
        collection_id: &str,
        document_id: S,
        precondition: Option<FirestoreWritePrecondition>,
        transforms: Vec<FirestoreFieldTransform>,
    ) -> FirestoreResult<&mut Self>
    where
        S: AsRef<str>,
    {
        self.add(TransformObjectOperation {
            parent: parent.to_string(),
            collection_id: collection_id.to_string(),
            document_id,
            precondition,
            transforms,
        })
    }
}