tiny_firestore_odm/
collection.rs1use crate::dynamic_firestore_client::SharedFirestoreClient;
2use crate::identifiers::{CollectionName, DocumentName, QualifyDocumentName};
3use crate::list_response::ListResponse;
4use firestore_serde::firestore::{
5 precondition::ConditionType, CreateDocumentRequest, DeleteDocumentRequest, GetDocumentRequest,
6 Precondition, UpdateDocumentRequest,
7};
8use serde::{de::DeserializeOwned, Serialize};
9use std::marker::PhantomData;
10use tonic::Code;
11
12pub struct Collection<T>
18where
19 T: Serialize + DeserializeOwned + 'static,
20{
21 db: SharedFirestoreClient,
22 name: CollectionName,
23 _ph: PhantomData<T>,
24}
25
26impl<T> Collection<T>
27where
28 T: Serialize + DeserializeOwned + Unpin,
29{
30 pub fn new(db: SharedFirestoreClient, name: CollectionName) -> Self {
32 Collection {
33 db,
34 name,
35 _ph: PhantomData::default(),
36 }
37 }
38
39 pub fn list(&self) -> ListResponse<T> {
41 ListResponse::new(self.name.clone(), self.db.clone())
42 }
43
44 pub fn name(&self) -> CollectionName {
45 self.name.clone()
46 }
47
48 pub fn subcollection<S>(&self, name: &str, collection: &str) -> Collection<S>
49 where
50 S: Serialize + DeserializeOwned + Unpin,
51 {
52 Collection {
53 db: self.db.clone(),
54 name: self.name.subcollection(name, collection),
55 _ph: PhantomData::default(),
56 }
57 }
58
59 pub async fn create_with_key(
63 &self,
64 ob: &T,
65 key: impl QualifyDocumentName,
66 ) -> anyhow::Result<()> {
67 let mut document = firestore_serde::to_document(ob)?;
68
69 document.name = key.qualify(&self.name)?.name();
70 self.db
71 .lock()
72 .await
73 .update_document(UpdateDocumentRequest {
74 document: Some(document),
75 current_document: Some(Precondition {
76 condition_type: Some(ConditionType::Exists(false)),
77 }),
78 ..UpdateDocumentRequest::default()
79 })
80 .await?;
81 Ok(())
82 }
83
84 pub async fn try_create(&self, ob: &T, key: impl QualifyDocumentName) -> anyhow::Result<bool> {
87 let mut document = firestore_serde::to_document(ob)?;
88 document.name = key.qualify(&self.name)?.name();
89 let result = self
90 .db
91 .lock()
92 .await
93 .update_document(UpdateDocumentRequest {
94 document: Some(document),
95 current_document: Some(Precondition {
96 condition_type: Some(ConditionType::Exists(false)),
97 }),
98 ..UpdateDocumentRequest::default()
99 })
100 .await;
101
102 match result {
103 Ok(_) => Ok(true),
104 Err(e) if e.code() == Code::AlreadyExists => Ok(false),
105 Err(e) => Err(e.into()),
106 }
107 }
108
109 pub async fn create(&self, ob: &T) -> anyhow::Result<DocumentName> {
111 let document = firestore_serde::to_document(ob)?;
112 let result = self
113 .db
114 .lock()
115 .await
116 .create_document(CreateDocumentRequest {
117 document: Some(document),
118 collection_id: self.name.leaf_name(),
119 parent: self.name.parent().name(),
120 ..CreateDocumentRequest::default()
121 })
122 .await?
123 .into_inner();
124 Ok(DocumentName::parse(&result.name)?)
125 }
126
127 pub async fn upsert(&self, ob: &T, key: impl QualifyDocumentName) -> anyhow::Result<()> {
129 let mut document = firestore_serde::to_document(ob)?;
130 document.name = key.qualify(&self.name)?.name();
131 self.db
132 .lock()
133 .await
134 .update_document(UpdateDocumentRequest {
135 document: Some(document),
136 ..UpdateDocumentRequest::default()
137 })
138 .await?;
139 Ok(())
140 }
141
142 pub async fn update(&self, ob: &T, key: impl QualifyDocumentName) -> anyhow::Result<()> {
144 let mut document = firestore_serde::to_document(ob)?;
145 document.name = key.qualify(&self.name)?.name();
146 self.db
147 .lock()
148 .await
149 .update_document(UpdateDocumentRequest {
150 document: Some(document),
151 current_document: Some(Precondition {
152 condition_type: Some(ConditionType::Exists(true)),
153 }),
154 ..UpdateDocumentRequest::default()
155 })
156 .await?;
157 Ok(())
158 }
159
160 pub async fn get(&self, key: impl QualifyDocumentName) -> anyhow::Result<T> {
162 let document = self
163 .db
164 .lock()
165 .await
166 .get_document(GetDocumentRequest {
167 name: key.qualify(&self.name)?.name(),
168 ..GetDocumentRequest::default()
169 })
170 .await?
171 .into_inner();
172
173 firestore_serde::from_document(document)
174 .map_err(|_| anyhow::anyhow!("Error deserializing."))
175 }
176
177 pub async fn delete(&self, key: impl QualifyDocumentName) -> anyhow::Result<()> {
179 let name = key.qualify(&self.name)?.name();
180 self.db
181 .lock()
182 .await
183 .delete_document(DeleteDocumentRequest {
184 name,
185 current_document: Some(Precondition {
186 condition_type: Some(ConditionType::Exists(true)),
187 }),
188 })
189 .await?;
190 Ok(())
191 }
192}