Skip to main content

firestore/
lib.rs

1//! # Firestore for Rust
2//!
3//! A client for Google Firestore built on the official gRPC API, with a fluent, strongly typed
4//! query builder and its own Serde serializer for Firestore's protobuf values.
5//!
6//! Full documentation, with a chapter per topic: <https://firestore-rust.abdolence.dev>
7//!
8//! ## Example
9//!
10//! ```rust,no_run
11//! use firestore::*;
12//! use serde::{Deserialize, Serialize};
13//!
14//! #[derive(Debug, Clone, Deserialize, Serialize)]
15//! struct MyTestStructure {
16//!     some_id: FirestoreDocumentId,
17//!     some_string: String,
18//!     some_num: u64,
19//! }
20//!
21//! # async fn example() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
22//! let db = FirestoreDb::new("my-project-id").await?;
23//!
24//! const TEST_COLLECTION_NAME: FirestoreCollectionId =
25//!     FirestoreCollectionId::from_static("test");
26//!
27//! let my_struct = MyTestStructure {
28//!     some_id: FirestoreDocumentId::from_static("test-1"),
29//!     some_string: "Test".to_string(),
30//!     some_num: 42,
31//! };
32//!
33//! // Create a document from a Rust structure
34//! let created: MyTestStructure = db.fluent()
35//!     .insert()
36//!     .into(TEST_COLLECTION_NAME)
37//!     .document_id(&my_struct.some_id)
38//!     .object(&my_struct)
39//!     .execute()
40//!     .await?;
41//!
42//! // Query, selecting fields by compile-time checked paths
43//! let as_vec: Vec<MyTestStructure> = db.fluent()
44//!     .select()
45//!     .fields(paths!(MyTestStructure::{some_id, some_num, some_string}))
46//!     .from(TEST_COLLECTION_NAME)
47//!     .filter(|q| q.for_all([
48//!         q.field(path!(MyTestStructure::some_num)).is_not_null(),
49//!         q.field(path!(MyTestStructure::some_string)).eq("Test"),
50//!     ]))
51//!     .order(|o| o.fields([o.field(path!(MyTestStructure::some_num)).desc()]))
52//!     .obj()
53//!     .query()
54//!     .await?;
55//!
56//! db.fluent()
57//!     .delete()
58//!     .from(TEST_COLLECTION_NAME)
59//!     .document_id(&my_struct.some_id)
60//!     .execute()
61//!     .await?;
62//! # Ok(())
63//! # }
64//! ```
65//!
66//! ## Where to look
67//!
68//! | For | Start at |
69//! |---|---|
70//! | Creating a client | [`FirestoreDb::new`], [`FirestoreDb::with_options`], [`FirestoreDb::with_options_auth`] |
71//! | Every read and write | [`FirestoreDb::fluent`] |
72//! | Document and collection IDs | [`FirestoreDocumentId`], [`FirestoreCollectionId`], [`ParentPathBuilder`] |
73//! | Field paths in queries and updates | [`path!`], [`paths!`] |
74//! | Transactions | [`FirestoreDb::run_transaction`], [`FirestoreTransaction`] |
75//! | Batch writes | [`FirestoreDb::create_simple_batch_writer`], [`FirestoreDb::create_streaming_batch_writer`] |
76//! | Realtime changes | [`FirestoreDb::create_listener`], [`FirestoreListener`] |
77//! | Timestamps | [`FirestoreTimestamp`], [`FirestoreInstant`], [`serialize_as_timestamp`] |
78//! | Schemaless documents | [`FirestoreDb::serialize_map_to_doc`], [`FirestoreValue`] |
79//! | Errors | [`errors`] |
80//!
81#![cfg_attr(
82    feature = "caching",
83    doc = "Caching is enabled in this build: see [`FirestoreCache`]."
84)]
85//!
86//! ## Cargo features
87//!
88//! | Feature | Effect |
89//! |---|---|
90//! | `tls-roots` (default) | TLS trust anchors from the platform's native root store |
91//! | `tls-webpki-roots` | TLS trust anchors bundled from the `webpki-roots` crate instead |
92//! | `auth-default-crypto` (default) | The aws-lc-rs rustls crypto provider service account keys sign their tokens with; without it, install a rustls `CryptoProvider` before creating a client |
93//! | `caching-memory` | In-memory collection and document cache, kept current by a listener |
94//! | `caching-persistent` | The same cache backed by an on-disk database |
95//! | `admin` | Declarative index management and bulk delete: composite indexes, vector indexes, single-field overrides, TTL policy, and `BulkDeleteDocuments` |
96//!
97//! Runnable examples for every topic:
98//! <https://github.com/abdolence/firestore-rs/tree/master/examples>
99
100#![allow(clippy::new_without_default)]
101#![allow(clippy::needless_lifetimes)]
102#![forbid(unsafe_code)]
103
104/// Defines the error types used throughout the `firestore-rs` crate.
105///
106/// This module contains the primary [`FirestoreError`] enum
107/// and various specific error structs that provide detailed information about
108/// issues encountered during Firestore operations.
109pub mod errors;
110
111mod firestore_value;
112
113pub use firestore_value::*;
114
115mod db;
116
117pub use db::*;
118
119mod firestore_serde;
120
121pub use firestore_serde::*;
122
123mod struct_path_macro;
124
125#[allow(unused_imports)]
126pub use struct_path_macro::*;
127
128/// Re-export of the [`macro@async_trait`] macro.
129///
130/// It is needed to implement the public async traits of this crate, such as
131/// [`FirestoreResumeStateStorage`] and the cache backend traits, without having to depend on
132/// `async-trait` directly.
133pub use async_trait::async_trait;
134
135/// Re-export of [`rvstruct::ValueStruct`].
136///
137/// This trait provides the `.value()` accessor on the newtypes of this crate, such as
138/// [`FirestoreListenerTarget`] and [`FirestoreListenerToken`], whose inner fields are private.
139/// Implementations of [`FirestoreResumeStateStorage`] need it to read those values.
140pub use rvstruct::ValueStruct;
141
142/// Re-export of the gcloud-sdk version this crate compiles against, for building
143/// [`gcloud_sdk::GoogleAuthHeaders`] and `google_cloud_auth` credentials.
144pub use gcloud_sdk;
145
146/// Re-export of the [`jiff`] crate, so that the date/time API used by this
147/// library is available without depending on `jiff` explicitly.
148pub use jiff;
149
150/// An exact instant in time, used by this library for Firestore timestamps.
151///
152/// This is an alias for [`jiff::Timestamp`]. Prefer this alias over naming
153/// `jiff::Timestamp` directly, so that your code does not need an explicit
154/// `jiff` dependency and stays insulated from changes of the underlying
155/// implementation.
156///
157/// Use it for the fields of your structures. To have a field serialized as a
158/// Firestore timestamp, either annotate it with one of the
159/// [`serialize_as_timestamp`] attributes, or wrap it in
160/// [`FirestoreTimestamp`], which does the same without an attribute.
161///
162/// # Examples
163///
164/// ```rust
165/// use firestore::*;
166/// use serde::{Deserialize, Serialize};
167///
168/// #[derive(Debug, Clone, Deserialize, Serialize)]
169/// struct MyTestStructure {
170///     #[serde(with = "firestore::serialize_as_timestamp")]
171///     created_at: FirestoreInstant,
172///
173///     #[serde(default)]
174///     #[serde(with = "firestore::serialize_as_optional_timestamp")]
175///     updated_at: Option<FirestoreInstant>,
176/// }
177/// ```
178pub type FirestoreInstant = jiff::Timestamp;
179
180/// The duration type used by this library.
181///
182/// This is an alias for [`jiff::SignedDuration`], used for values such as
183/// [`FirestoreTransactionOptions::max_elapsed_time`] and the query execution
184/// statistics.
185pub type FirestoreDuration = jiff::SignedDuration;
186
187/// Provides utility functions for working with Firestore timestamps.
188///
189/// This module includes helpers for converting between [`FirestoreInstant`]
190/// and Google's `Timestamp` protobuf type, often used with `#[serde(with)]`
191/// attributes for automatic conversion.
192pub mod timestamp_utils;
193
194use crate::errors::FirestoreError;
195
196/// A type alias for `std::result::Result<T, FirestoreError>`.
197///
198/// This is the standard result type used throughout the `firestore-rs` crate
199/// for operations that can fail, encapsulating either a successful value `T`
200/// or a [`FirestoreError`].
201pub type FirestoreResult<T> = std::result::Result<T, FirestoreError>;
202
203/// A type alias for the raw Firestore document representation.
204///
205/// This refers to `gcloud_sdk::google::firestore::v1::Document`, which is the
206/// underlying gRPC/protobuf structure for a Firestore document.
207pub type FirestoreDocument = gcloud_sdk::google::firestore::v1::Document;
208
209/// The kind of change a listener reports for one of its targets.
210///
211/// This refers to `gcloud_sdk::google::firestore::v1::target_change::TargetChangeType`, and is
212/// re-exported so that a
213/// [`FirestoreListenEvent::TargetChange`](crate::FirestoreListenEvent::TargetChange) can be
214/// matched without depending on `gcloud-sdk` directly:
215///
216/// ```rust,no_run
217/// # use firestore::*;
218/// # fn example(event: FirestoreListenEvent) {
219/// if let FirestoreListenEvent::TargetChange(target_change) = event {
220///     match FirestoreListenerTargetChangeType::try_from(target_change.target_change_type) {
221///         // The target now reflects a consistent snapshot.
222///         Ok(FirestoreListenerTargetChangeType::Current) => {}
223///         // With a `cause`, Firestore removed the target on its own. With none, it
224///         // acknowledges `remove_target`, or ends a target added once.
225///         Ok(FirestoreListenerTargetChangeType::Remove) => {}
226///         _ => {}
227///     }
228/// }
229/// # }
230/// ```
231pub type FirestoreListenerTargetChangeType =
232    gcloud_sdk::google::firestore::v1::target_change::TargetChangeType;
233
234mod firestore_meta;
235
236pub use firestore_meta::*;
237
238mod firestore_document_functions;
239
240pub use firestore_document_functions::*;
241
242mod fluent_api;
243
244pub use fluent_api::*;
245
246/// The crate backing [`path!`] and [`paths!`].
247///
248/// Re-exported because those macros expand to paths into it, so a caller cannot use them
249/// without it being in scope.
250pub extern crate struct_path;
251
252#[cfg(feature = "caching")]
253mod cache;
254
255#[cfg(feature = "caching")]
256pub use cache::*;
257
258#[cfg(doctest)]
259mod book;