bonsaidb_core/schema/collection.rs
1use std::borrow::{Borrow, Cow};
2use std::fmt::Debug;
3use std::marker::PhantomData;
4use std::task::Poll;
5
6use async_trait::async_trait;
7use futures::future::BoxFuture;
8use futures::{ready, Future, FutureExt};
9use serde::de::DeserializeOwned;
10use serde::{Deserialize, Serialize};
11use transmog::{Format, OwnedDeserializer};
12use transmog_pot::Pot;
13
14use crate::connection::{self, AsyncConnection, Connection, RangeRef};
15use crate::document::{
16 BorrowedDocument, CollectionDocument, CollectionHeader, Document, DocumentId, Header, KeyId,
17 OwnedDocument, OwnedDocuments, Revision,
18};
19use crate::key::{IntoPrefixRange, Key, KeyEncoding};
20use crate::schema::{CollectionName, Schematic};
21use crate::transaction::{Operation, OperationResult, Transaction};
22use crate::Error;
23
24/// A namespaced collection of `Document<Self>` items and views.
25///
26/// ## Deriving this trait
27///
28/// This trait can be derived instead of manually implemented:
29///
30/// ```rust
31/// use bonsaidb_core::schema::Collection;
32/// use serde::{Deserialize, Serialize};
33///
34/// #[derive(Serialize, Deserialize, Default, Collection)]
35/// #[collection(name = "MyCollection")]
36/// # #[collection(core = bonsaidb_core)]
37/// pub struct MyCollection;
38/// ```
39///
40/// If you're publishing a collection for use in multiple projects, consider
41/// giving the collection an `authority`, which gives your collection a
42/// namespace:
43///
44/// ```rust
45/// use bonsaidb_core::schema::Collection;
46/// use serde::{Deserialize, Serialize};
47///
48/// #[derive(Serialize, Deserialize, Default, Collection)]
49/// #[collection(name = "MyCollection", authority = "khonsulabs")]
50/// # #[collection(core = bonsaidb_core)]
51/// pub struct MyCollection;
52/// ```
53///
54/// The list of views can be specified using the `views` parameter:
55///
56/// ```rust
57/// use bonsaidb_core::schema::{Collection, View, ViewSchema};
58/// use serde::{Deserialize, Serialize};
59///
60/// #[derive(Serialize, Deserialize, Default, Collection)]
61/// #[collection(name = "MyCollection", views = [ScoresByRank])]
62/// # #[collection(core = bonsaidb_core)]
63/// pub struct MyCollection;
64///
65/// #[derive(Clone, View, ViewSchema)]
66/// #[view(collection = MyCollection, key = u32, value = f32, name = "scores-by-rank")]
67/// # #[view(core = bonsaidb_core)]
68/// # #[view_schema(core = bonsaidb_core)]
69/// pub struct ScoresByRank;
70/// #
71/// # use bonsaidb_core::{
72/// # document::CollectionDocument,
73/// # schema::{
74/// # ReduceResult, CollectionMapReduce,
75/// # ViewMapResult, ViewMappedValue,
76/// # },
77/// # };
78/// # impl CollectionMapReduce for ScoresByRank {
79/// # fn map<'doc>(
80/// # &self,
81/// # _document: CollectionDocument<<Self::View as View>::Collection>,
82/// # ) -> ViewMapResult<'doc, Self::View> {
83/// # todo!()
84/// # }
85/// #
86/// # fn reduce(
87/// # &self,
88/// # _mappings: &[ViewMappedValue<'_, Self::View>],
89/// # _rereduce: bool,
90/// # ) -> ReduceResult<Self::View> {
91/// # todo!()
92/// # }
93/// # }
94/// ```
95///
96/// ### Selecting a Primary Key type
97///
98/// By default, the `#[collection]` macro will use `u64` for the
99/// [`Self::PrimaryKey`] type. Collections can use any type that implements the
100/// [`Key`] trait:
101///
102/// ```rust
103/// use bonsaidb_core::schema::Collection;
104/// use serde::{Deserialize, Serialize};
105///
106/// #[derive(Serialize, Deserialize, Default, Collection)]
107/// #[collection(name = "MyCollection", primary_key = u128)]
108/// # #[collection(core = bonsaidb_core)]
109/// pub struct MyCollection;
110/// ```
111///
112/// If the data being stored has a ["natural key"][natural-key], the field can
113/// be annotated with `#[natural_id]` to use the field's contents as the primary
114/// key when doing a push operation:
115///
116/// ```rust
117/// use bonsaidb_core::schema::Collection;
118/// use serde::{Deserialize, Serialize};
119///
120/// #[derive(Serialize, Deserialize, Default, Collection)]
121/// #[collection(name = "MyCollection")]
122/// # #[collection(core = bonsaidb_core)]
123/// pub struct MyCollection {
124/// #[natural_id]
125/// pub external_id: u64,
126/// }
127/// ```
128///
129/// Alternatively, this can be accomplished with an expression using the
130/// `natural_id` attribute:
131///
132/// ```rust
133/// use bonsaidb_core::schema::Collection;
134/// use serde::{Deserialize, Serialize};
135///
136/// #[derive(Serialize, Deserialize, Default, Collection)]
137/// #[collection(name = "MyCollection", natural_id = Some(self.external_id))]
138/// # #[collection(core = bonsaidb_core)]
139/// pub struct MyCollection {
140/// pub external_id: u64,
141/// }
142/// ```
143///
144/// Primary keys are not able to be updated. To update a document's primary key,
145/// the contents must be inserted at the new id and deleted from the previous
146/// id.
147///
148/// [natural-key]: https://en.wikipedia.org/wiki/Natural_key
149///
150///
151/// ### Specifying a Collection Encryption Key
152///
153/// By default, encryption will be required if an `encryption_key` is provided:
154///
155/// ```rust
156/// use bonsaidb_core::document::KeyId;
157/// use bonsaidb_core::schema::Collection;
158/// use serde::{Deserialize, Serialize};
159///
160/// #[derive(Serialize, Deserialize, Default, Collection)]
161/// #[collection(name = "MyCollection", encryption_key = Some(KeyId::Master))]
162/// # #[collection(core = bonsaidb_core)]
163/// pub struct MyCollection;
164/// ```
165///
166/// The `encryption_required` parameter can be provided if you wish to be
167/// explicit:
168///
169/// ```rust
170/// use bonsaidb_core::document::KeyId;
171/// use bonsaidb_core::schema::Collection;
172/// use serde::{Deserialize, Serialize};
173///
174/// #[derive(Serialize, Deserialize, Default, Collection)]
175/// #[collection(name = "MyCollection")]
176/// #[collection(encryption_key = Some(KeyId::Master), encryption_required)]
177/// # #[collection(core = bonsaidb_core)]
178/// pub struct MyCollection;
179/// ```
180///
181/// Or, if you wish your collection to be encrypted if its available, but not
182/// cause errors when being stored without encryption, you can provide the
183/// `encryption_optional` parameter:
184///
185/// ```rust
186/// use bonsaidb_core::document::KeyId;
187/// use bonsaidb_core::schema::Collection;
188/// use serde::{Deserialize, Serialize};
189///
190/// #[derive(Serialize, Deserialize, Default, Collection)]
191/// #[collection(name = "MyCollection")]
192/// #[collection(encryption_key = Some(KeyId::Master), encryption_optional)]
193/// # #[collection(core = bonsaidb_core)]
194/// pub struct MyCollection;
195/// ```
196///
197/// ### Changing the serialization strategy
198///
199/// BonsaiDb uses [`transmog`](https://github.com/khonsulabs/transmog) to allow
200/// customizing serialization formats. To use one of the formats Transmog
201/// already supports, add its crate to your Cargo.toml and use it like this
202/// example using `transmog_bincode`:
203///
204/// ```rust
205/// use bonsaidb_core::schema::Collection;
206/// use serde::{Deserialize, Serialize};
207///
208/// #[derive(Serialize, Deserialize, Default, Collection)]
209/// #[collection(name = "MyCollection")]
210/// #[collection(serialization = transmog_bincode::Bincode)]
211/// # #[collection(core = bonsaidb_core)]
212/// pub struct MyCollection;
213/// ```
214///
215/// To manually implement `SerializedCollection` you can pass `None` to
216/// `serialization`:
217///
218/// ```rust
219/// use bonsaidb_core::schema::Collection;
220///
221/// #[derive(Default, Collection)]
222/// #[collection(name = "MyCollection")]
223/// #[collection(serialization = None)]
224/// # #[collection(core = bonsaidb_core)]
225/// pub struct MyCollection;
226/// ```
227///
228/// If the collection type implements or derives the [`Key`](crate::key::Key)
229/// trait, `serialization = Key` can be passed to serialize using the [key
230/// format](crate::key::KeyFormat).
231pub trait Collection: Send + Sync {
232 /// The unique id type. Each document stored in a collection will be
233 /// uniquely identified by this type.
234 ///
235 /// ## Primary Key Limits
236 ///
237 /// The result of [`KeyEncoding::as_ord_bytes()`] must be less than or equal
238 /// to [`DocumentId::MAX_LENGTH`].
239 type PrimaryKey: for<'k> Key<'k> + Eq + Ord;
240
241 /// The unique name of this collection. Each collection must be uniquely
242 /// named within the [`Schema`](crate::schema::Schema) it is registered
243 /// within.
244 fn collection_name() -> CollectionName;
245
246 /// Defines all `View`s in this collection in `schema`.
247 fn define_views(schema: &mut Schematic) -> Result<(), Error>;
248
249 /// If a [`KeyId`] is returned, this collection will be stored encrypted
250 /// at-rest using the key specified.
251 #[must_use]
252 fn encryption_key() -> Option<KeyId> {
253 None
254 }
255}
256
257/// A collection that knows how to serialize and deserialize documents to an associated type.
258///
259/// These examples for this type use this basic collection definition:
260///
261/// ```rust
262/// use bonsaidb_core::schema::{Collection, DefaultSerialization, Schematic};
263/// use bonsaidb_core::Error;
264/// use serde::{Deserialize, Serialize};
265///
266/// #[derive(Debug, Serialize, Deserialize, Default, Collection)]
267/// #[collection(name = "MyCollection")]
268/// # #[collection(core = bonsaidb_core)]
269/// pub struct MyCollection {
270/// pub rank: u32,
271/// pub score: f32,
272/// }
273/// ```
274#[async_trait]
275pub trait SerializedCollection: Collection {
276 /// The type of the contents stored in documents in this collection.
277 type Contents: Send + Sync;
278 /// The serialization format for this collection.
279 type Format: OwnedDeserializer<Self::Contents>;
280
281 /// Returns the natural identifier of `contents`. This is called when
282 /// pushing values into a collection, before attempting to automatically
283 /// assign a unique id.
284 #[allow(unused_variables)]
285 fn natural_id(contents: &Self::Contents) -> Option<Self::PrimaryKey>
286 where
287 Self: Sized,
288 {
289 None
290 }
291
292 /// Returns the configured instance of [`Self::Format`].
293 // TODO allow configuration to be passed here, such as max allocation bytes.
294 fn format() -> Self::Format;
295
296 /// Deserialize `data` as `Self::Contents` using this collection's format.
297 fn deserialize(data: &[u8]) -> Result<Self::Contents, Error> {
298 Self::format()
299 .deserialize_owned(data)
300 .map_err(|err| crate::Error::other("serialization", err))
301 }
302
303 /// Returns the deserialized contents of `doc`.
304 fn document_contents<D: Document<Self>>(doc: &D) -> Result<Self::Contents, Error>
305 where
306 Self: Sized,
307 {
308 doc.contents()
309 }
310
311 /// Sets the contents of `doc` to `contents`.
312 fn set_document_contents<D: Document<Self>>(
313 doc: &mut D,
314 contents: Self::Contents,
315 ) -> Result<(), Error>
316 where
317 Self: Sized,
318 {
319 doc.set_contents(contents)
320 }
321
322 /// Serialize `item` using this collection's format.
323 fn serialize(item: &Self::Contents) -> Result<Vec<u8>, Error> {
324 Self::format()
325 .serialize(item)
326 .map_err(|err| crate::Error::other("serialization", err))
327 }
328
329 /// Gets a [`CollectionDocument`] with `id` from `connection`.
330 ///
331 /// ```rust
332 /// # bonsaidb_core::__doctest_prelude!();
333 /// # use bonsaidb_core::connection::Connection;
334 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
335 /// if let Some(doc) = MyCollection::get(&42, &db)? {
336 /// println!(
337 /// "Retrieved revision {} with deserialized contents: {:?}",
338 /// doc.header.revision, doc.contents
339 /// );
340 /// }
341 /// # Ok(())
342 /// # }
343 /// ```
344 fn get<C, PrimaryKey>(
345 id: &PrimaryKey,
346 connection: &C,
347 ) -> Result<Option<CollectionDocument<Self>>, Error>
348 where
349 C: Connection,
350 PrimaryKey: KeyEncoding<Self::PrimaryKey>,
351 Self: Sized,
352 {
353 let possible_doc = connection.get::<Self, _>(id)?;
354 possible_doc.as_ref().map(TryInto::try_into).transpose()
355 }
356
357 /// Gets a [`CollectionDocument`] with `id` from `connection`.
358 ///
359 /// ```rust
360 /// # bonsaidb_core::__doctest_prelude!();
361 /// # use bonsaidb_core::connection::AsyncConnection;
362 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
363 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
364 /// if let Some(doc) = MyCollection::get_async(&42, &db).await? {
365 /// println!(
366 /// "Retrieved revision {} with deserialized contents: {:?}",
367 /// doc.header.revision, doc.contents
368 /// );
369 /// }
370 /// # Ok(())
371 /// # })
372 /// # }
373 /// ```
374 async fn get_async<C, PrimaryKey>(
375 id: &PrimaryKey,
376 connection: &C,
377 ) -> Result<Option<CollectionDocument<Self>>, Error>
378 where
379 C: AsyncConnection,
380 PrimaryKey: KeyEncoding<Self::PrimaryKey>,
381 Self: Sized,
382 {
383 let possible_doc = connection.get::<Self, _>(id).await?;
384 Ok(possible_doc.as_ref().map(TryInto::try_into).transpose()?)
385 }
386
387 /// Retrieves all documents matching `ids`. Documents that are not found
388 /// are not returned, but no error will be generated.
389 ///
390 /// ```rust
391 /// # bonsaidb_core::__doctest_prelude!();
392 /// # use bonsaidb_core::connection::Connection;
393 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
394 /// for doc in MyCollection::get_multiple(&[42, 43], &db)? {
395 /// println!(
396 /// "Retrieved #{} with deserialized contents: {:?}",
397 /// doc.header.id, doc.contents
398 /// );
399 /// }
400 /// # Ok(())
401 /// # }
402 /// ```
403 fn get_multiple<'id, C, DocumentIds, PrimaryKey, I>(
404 ids: DocumentIds,
405 connection: &C,
406 ) -> Result<Vec<CollectionDocument<Self>>, Error>
407 where
408 C: Connection,
409 DocumentIds: IntoIterator<Item = &'id PrimaryKey, IntoIter = I> + Send + Sync,
410 I: Iterator<Item = &'id PrimaryKey> + Send + Sync,
411 PrimaryKey: KeyEncoding<Self::PrimaryKey> + 'id,
412 Self: Sized,
413 {
414 connection
415 .collection::<Self>()
416 .get_multiple(ids)
417 .and_then(|docs| docs.collection_documents())
418 }
419
420 /// Retrieves all documents matching `ids`. Documents that are not found
421 /// are not returned, but no error will be generated.
422 ///
423 /// ```rust
424 /// # bonsaidb_core::__doctest_prelude!();
425 /// # use bonsaidb_core::connection::AsyncConnection;
426 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
427 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
428 /// for doc in MyCollection::get_multiple_async(&[42, 43], &db).await? {
429 /// println!(
430 /// "Retrieved #{} with deserialized contents: {:?}",
431 /// doc.header.id, doc.contents
432 /// );
433 /// }
434 /// # Ok(())
435 /// # })
436 /// # }
437 /// ```
438 async fn get_multiple_async<'id, C, DocumentIds, PrimaryKey, I>(
439 ids: DocumentIds,
440 connection: &C,
441 ) -> Result<Vec<CollectionDocument<Self>>, Error>
442 where
443 C: AsyncConnection,
444 DocumentIds: IntoIterator<Item = &'id PrimaryKey, IntoIter = I> + Send + Sync,
445 I: Iterator<Item = &'id PrimaryKey> + Send + Sync,
446 PrimaryKey: KeyEncoding<Self::PrimaryKey> + 'id,
447 Self: Sized,
448 {
449 connection
450 .collection::<Self>()
451 .get_multiple(ids)
452 .await
453 .and_then(|docs| docs.collection_documents())
454 }
455
456 /// Retrieves all documents matching the range of `ids`.
457 ///
458 /// ```rust
459 /// # bonsaidb_core::__doctest_prelude!();
460 /// # use bonsaidb_core::connection::Connection;
461 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
462 /// for doc in MyCollection::list(42.., &db)
463 /// .descending()
464 /// .limit(20)
465 /// .query()?
466 /// {
467 /// println!(
468 /// "Retrieved #{} with deserialized contents: {:?}",
469 /// doc.header.id, doc.contents
470 /// );
471 /// }
472 /// # Ok(())
473 /// # }
474 /// ```
475 fn list<'id, R, PrimaryKey, C>(ids: R, connection: &'id C) -> List<'id, C, Self, PrimaryKey>
476 where
477 R: Into<RangeRef<'id, Self::PrimaryKey, PrimaryKey>>,
478 C: Connection,
479 PrimaryKey: KeyEncoding<Self::PrimaryKey> + PartialEq + 'id,
480 Self::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
481 Self: Sized,
482 {
483 List(connection::List::new(
484 connection::MaybeOwned::Owned(connection.collection::<Self>()),
485 ids.into(),
486 ))
487 }
488
489 /// Retrieves all documents matching the range of `ids`.
490 ///
491 /// ```rust
492 /// # bonsaidb_core::__doctest_prelude!();
493 /// # use bonsaidb_core::connection::AsyncConnection;
494 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
495 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
496 /// for doc in MyCollection::list_async(42.., &db)
497 /// .descending()
498 /// .limit(20)
499 /// .await?
500 /// {
501 /// println!(
502 /// "Retrieved #{} with deserialized contents: {:?}",
503 /// doc.header.id, doc.contents
504 /// );
505 /// }
506 /// # Ok(())
507 /// # })
508 /// # }
509 /// ```
510 fn list_async<'id, R, PrimaryKey, C>(
511 ids: R,
512 connection: &'id C,
513 ) -> AsyncList<'id, C, Self, PrimaryKey>
514 where
515 R: Into<RangeRef<'id, Self::PrimaryKey, PrimaryKey>>,
516 C: AsyncConnection,
517 PrimaryKey: KeyEncoding<Self::PrimaryKey> + PartialEq + 'id + ?Sized,
518 Self::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
519 Self: Sized,
520 {
521 AsyncList(connection::AsyncList::new(
522 connection::MaybeOwned::Owned(connection.collection::<Self>()),
523 ids.into(),
524 ))
525 }
526
527 /// Retrieves all documents with ids that start with `prefix`.
528 ///
529 /// ```rust
530 /// use bonsaidb_core::connection::Connection;
531 /// use bonsaidb_core::document::CollectionDocument;
532 /// use bonsaidb_core::schema::{Collection, Schematic, SerializedCollection};
533 /// use bonsaidb_core::Error;
534 /// use serde::{Deserialize, Serialize};
535 ///
536 /// #[derive(Debug, Serialize, Deserialize, Default, Collection)]
537 /// #[collection(name = "MyCollection", primary_key = String)]
538 /// # #[collection(core = bonsaidb_core)]
539 /// pub struct MyCollection;
540 ///
541 /// async fn starts_with_a<C: Connection>(
542 /// db: &C,
543 /// ) -> Result<Vec<CollectionDocument<MyCollection>>, Error> {
544 /// MyCollection::list_with_prefix("a", db).query()
545 /// }
546 /// ```
547 fn list_with_prefix<'a, PrimaryKey, C>(
548 prefix: &'a PrimaryKey,
549 connection: &'a C,
550 ) -> List<'a, C, Self, PrimaryKey>
551 where
552 C: Connection,
553 Self: Sized,
554 PrimaryKey: IntoPrefixRange<'a, Self::PrimaryKey>
555 + KeyEncoding<Self::PrimaryKey>
556 + PartialEq
557 + ?Sized,
558 Self::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
559 {
560 List(connection::List::new(
561 connection::MaybeOwned::Owned(connection.collection::<Self>()),
562 prefix.to_prefix_range(),
563 ))
564 }
565
566 /// Retrieves all documents with ids that start with `prefix`.
567 ///
568 /// ```rust
569 /// use bonsaidb_core::connection::AsyncConnection;
570 /// use bonsaidb_core::document::CollectionDocument;
571 /// use bonsaidb_core::schema::{Collection, Schematic, SerializedCollection};
572 /// use bonsaidb_core::Error;
573 /// use serde::{Deserialize, Serialize};
574 ///
575 /// #[derive(Debug, Serialize, Deserialize, Default, Collection)]
576 /// #[collection(name = "MyCollection", primary_key = String)]
577 /// # #[collection(core = bonsaidb_core)]
578 /// pub struct MyCollection;
579 ///
580 /// async fn starts_with_a<C: AsyncConnection>(
581 /// db: &C,
582 /// ) -> Result<Vec<CollectionDocument<MyCollection>>, Error> {
583 /// MyCollection::list_with_prefix_async("a", db).await
584 /// }
585 /// ```
586 fn list_with_prefix_async<'a, PrimaryKey, C>(
587 prefix: &'a PrimaryKey,
588 connection: &'a C,
589 ) -> AsyncList<'a, C, Self, PrimaryKey>
590 where
591 C: AsyncConnection,
592 Self: Sized,
593 PrimaryKey: IntoPrefixRange<'a, Self::PrimaryKey>
594 + KeyEncoding<Self::PrimaryKey>
595 + PartialEq
596 + ?Sized,
597 Self::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
598 {
599 AsyncList(connection::AsyncList::new(
600 connection::MaybeOwned::Owned(connection.collection::<Self>()),
601 prefix.to_prefix_range(),
602 ))
603 }
604
605 /// Retrieves all documents.
606 ///
607 /// ```rust
608 /// # bonsaidb_core::__doctest_prelude!();
609 /// # use bonsaidb_core::connection::Connection;
610 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
611 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
612 /// for doc in MyCollection::all(&db).query()? {
613 /// println!(
614 /// "Retrieved #{} with deserialized contents: {:?}",
615 /// doc.header.id, doc.contents
616 /// );
617 /// }
618 /// # Ok(())
619 /// # })
620 /// # }
621 /// ```
622 fn all<C: Connection>(connection: &C) -> List<'_, C, Self, Self::PrimaryKey>
623 where
624 Self: Sized,
625 {
626 List(connection::List::new(
627 connection::MaybeOwned::Owned(connection.collection::<Self>()),
628 RangeRef::from(..),
629 ))
630 }
631
632 /// Retrieves all documents.
633 ///
634 /// ```rust
635 /// # bonsaidb_core::__doctest_prelude!();
636 /// # use bonsaidb_core::connection::AsyncConnection;
637 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
638 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
639 /// for doc in MyCollection::all_async(&db).await? {
640 /// println!(
641 /// "Retrieved #{} with deserialized contents: {:?}",
642 /// doc.header.id, doc.contents
643 /// );
644 /// }
645 /// # Ok(())
646 /// # })
647 /// # }
648 /// ```
649 fn all_async<C: AsyncConnection>(connection: &C) -> AsyncList<'_, C, Self, Self::PrimaryKey>
650 where
651 Self: Sized,
652 {
653 AsyncList(connection::AsyncList::new(
654 connection::MaybeOwned::Owned(connection.collection::<Self>()),
655 RangeRef::from(..),
656 ))
657 }
658
659 /// Pushes this value into the collection, returning the created document.
660 /// This function is useful when `Self != Self::Contents`.
661 ///
662 /// ## Automatic ID Assignment
663 ///
664 /// This function calls [`Self::natural_id()`] to try to retrieve a primary
665 /// key value from `contents`. If an id is returned, the item is inserted
666 /// with that id. If an id is not returned, an id will be automatically
667 /// assigned, if possible, by the storage backend, which uses the [`Key`]
668 /// trait to assign ids.
669 ///
670 /// ```rust
671 /// # bonsaidb_core::__doctest_prelude!();
672 /// # use bonsaidb_core::connection::Connection;
673 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
674 /// let document = MyCollection::push(MyCollection::default(), &db)?;
675 /// println!(
676 /// "Inserted {:?} with id {} with revision {}",
677 /// document.contents, document.header.id, document.header.revision
678 /// );
679 /// # Ok(())
680 /// # }
681 /// ```
682 fn push<Cn: Connection>(
683 contents: Self::Contents,
684 connection: &Cn,
685 ) -> Result<CollectionDocument<Self>, InsertError<Self::Contents>>
686 where
687 Self: Sized + 'static,
688 {
689 let header = match connection.collection::<Self>().push(&contents) {
690 Ok(header) => header,
691 Err(error) => return Err(InsertError { contents, error }),
692 };
693 Ok(CollectionDocument { header, contents })
694 }
695
696 /// Pushes this value into the collection, returning the created document.
697 /// This function is useful when `Self != Self::Contents`.
698 ///
699 /// ## Automatic ID Assignment
700 ///
701 /// This function calls [`Self::natural_id()`] to try to retrieve a primary
702 /// key value from `contents`. If an id is returned, the item is inserted
703 /// with that id. If an id is not returned, an id will be automatically
704 /// assigned, if possible, by the storage backend, which uses the [`Key`]
705 /// trait to assign ids.
706 ///
707 /// ```rust
708 /// # bonsaidb_core::__doctest_prelude!();
709 /// # use bonsaidb_core::connection::AsyncConnection;
710 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
711 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
712 /// let document = MyCollection::push_async(MyCollection::default(), &db).await?;
713 /// println!(
714 /// "Inserted {:?} with id {} with revision {}",
715 /// document.contents, document.header.id, document.header.revision
716 /// );
717 /// # Ok(())
718 /// # })
719 /// # }
720 /// ```
721 async fn push_async<Cn: AsyncConnection>(
722 contents: Self::Contents,
723 connection: &Cn,
724 ) -> Result<CollectionDocument<Self>, InsertError<Self::Contents>>
725 where
726 Self: Sized + 'static,
727 Self::Contents: 'async_trait,
728 {
729 let header = match connection.collection::<Self>().push(&contents).await {
730 Ok(header) => header,
731 Err(error) => return Err(InsertError { contents, error }),
732 };
733 Ok(CollectionDocument { header, contents })
734 }
735
736 /// Pushes all `contents` in a single transaction. If successful, all
737 /// collection documents will be returned. If an error occurs during this
738 /// operation, no documents will be pushed.
739 ///
740 /// ## Automatic ID Assignment
741 ///
742 /// This function calls [`Self::natural_id()`] to try to retrieve a primary
743 /// key value from each instance of `contents`. If an id is returned, the
744 /// item is inserted with that id. If an id is not returned, an id will be
745 /// automatically assigned, if possible, by the storage backend, which uses
746 /// the [`Key`] trait to assign ids.
747 ///
748 /// ```rust
749 /// # bonsaidb_core::__doctest_prelude!();
750 /// # use bonsaidb_core::connection::Connection;
751 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
752 /// let documents = MyCollection::push_all(
753 /// [
754 /// MyCollection::default(),
755 /// MyCollection::default(),
756 /// MyCollection::default(),
757 /// ],
758 /// &db,
759 /// )?;
760 /// for document in documents {
761 /// println!(
762 /// "Inserted {:?} with id {} with revision {}",
763 /// document.contents, document.header.id, document.header.revision
764 /// );
765 /// }
766 /// # Ok(())
767 /// # }
768 /// ```
769 fn push_all<Contents: IntoIterator<Item = Self::Contents>, Cn: Connection>(
770 contents: Contents,
771 connection: &Cn,
772 ) -> Result<Vec<CollectionDocument<Self>>, Error>
773 where
774 Self: Sized + 'static,
775 Self::PrimaryKey: Default,
776 {
777 let mut tx = Transaction::new();
778 let contents = contents.into_iter();
779 let mut results = Vec::with_capacity(contents.size_hint().0);
780 for contents in contents {
781 tx.push(Operation::push_serialized::<Self>(&contents)?);
782 results.push(CollectionDocument {
783 header: CollectionHeader {
784 id: <<Self as Collection>::PrimaryKey as Default>::default(),
785 revision: Revision {
786 id: 0,
787 sha256: [0; 32],
788 },
789 },
790 contents,
791 });
792 }
793 for (result, document) in tx.apply(connection)?.into_iter().zip(&mut results) {
794 match result {
795 OperationResult::DocumentUpdated { header, .. } => {
796 document.header = CollectionHeader::try_from(header)?;
797 }
798 _ => unreachable!("invalid result from transaction"),
799 }
800 }
801 Ok(results)
802 }
803
804 /// Pushes all `contents` in a single transaction. If successful, all
805 /// collection documents will be returned. If an error occurs during this
806 /// operation, no documents will be pushed.
807 ///
808 /// ## Automatic ID Assignment
809 ///
810 /// This function calls [`Self::natural_id()`] to try to retrieve a primary
811 /// key value from each instance of `contents`. If an id is returned, the
812 /// item is inserted with that id. If an id is not returned, an id will be
813 /// automatically assigned, if possible, by the storage backend, which uses
814 /// the [`Key`] trait to assign ids.
815 ///
816 /// ```rust
817 /// # bonsaidb_core::__doctest_prelude!();
818 /// # use bonsaidb_core::connection::AsyncConnection;
819 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
820 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
821 /// let documents = MyCollection::push_all_async(
822 /// [
823 /// MyCollection::default(),
824 /// MyCollection::default(),
825 /// MyCollection::default(),
826 /// ],
827 /// &db,
828 /// )
829 /// .await?;
830 /// for document in documents {
831 /// println!(
832 /// "Inserted {:?} with id {} with revision {}",
833 /// document.contents, document.header.id, document.header.revision
834 /// );
835 /// }
836 /// # Ok(())
837 /// # })}
838 /// ```
839 async fn push_all_async<
840 Contents: IntoIterator<Item = Self::Contents> + Send,
841 Cn: AsyncConnection,
842 >(
843 contents: Contents,
844 connection: &Cn,
845 ) -> Result<Vec<CollectionDocument<Self>>, Error>
846 where
847 Self: Sized + 'static,
848 Self::PrimaryKey: Default,
849 Contents::IntoIter: Send,
850 {
851 let mut tx = Transaction::new();
852 let contents = contents.into_iter();
853 let mut results = Vec::with_capacity(contents.size_hint().0);
854 for contents in contents {
855 tx.push(Operation::push_serialized::<Self>(&contents)?);
856 results.push(CollectionDocument {
857 header: CollectionHeader {
858 id: <<Self as Collection>::PrimaryKey as Default>::default(),
859 revision: Revision {
860 id: 0,
861 sha256: [0; 32],
862 },
863 },
864 contents,
865 });
866 }
867 for (result, document) in tx
868 .apply_async(connection)
869 .await?
870 .into_iter()
871 .zip(&mut results)
872 {
873 match result {
874 OperationResult::DocumentUpdated { header, .. } => {
875 document.header = CollectionHeader::try_from(header)?;
876 }
877 _ => unreachable!("invalid result from transaction"),
878 }
879 }
880 Ok(results)
881 }
882
883 /// Pushes this value into the collection, returning the created document.
884 ///
885 /// ## Automatic ID Assignment
886 ///
887 /// This function calls [`Self::natural_id()`] to try to retrieve a primary
888 /// key value from `self`. If an id is returned, the item is inserted with
889 /// that id. If an id is not returned, an id will be automatically assigned,
890 /// if possible, by the storage backend, which uses the [`Key`] trait to
891 /// assign ids.
892 ///
893 /// ```rust
894 /// # bonsaidb_core::__doctest_prelude!();
895 /// # use bonsaidb_core::connection::Connection;
896 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
897 /// let document = MyCollection::default().push_into(&db)?;
898 /// println!(
899 /// "Inserted {:?} with id {} with revision {}",
900 /// document.contents, document.header.id, document.header.revision
901 /// );
902 /// # Ok(())
903 /// # }
904 /// ```
905 fn push_into<Cn: Connection>(
906 self,
907 connection: &Cn,
908 ) -> Result<CollectionDocument<Self>, InsertError<Self>>
909 where
910 Self: SerializedCollection<Contents = Self> + Sized + 'static,
911 {
912 Self::push(self, connection)
913 }
914
915 /// Pushes this value into the collection, returning the created document.
916 ///
917 /// ## Automatic ID Assignment
918 ///
919 /// This function calls [`Self::natural_id()`] to try to retrieve a primary
920 /// key value from `self`. If an id is returned, the item is inserted with
921 /// that id. If an id is not returned, an id will be automatically assigned,
922 /// if possible, by the storage backend, which uses the [`Key`] trait to
923 /// assign ids.
924 ///
925 /// ```rust
926 /// # bonsaidb_core::__doctest_prelude!();
927 /// # use bonsaidb_core::connection::AsyncConnection;
928 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
929 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
930 /// let document = MyCollection::default().push_into_async(&db).await?;
931 /// println!(
932 /// "Inserted {:?} with id {} with revision {}",
933 /// document.contents, document.header.id, document.header.revision
934 /// );
935 /// # Ok(())
936 /// # })
937 /// # }
938 /// ```
939 async fn push_into_async<Cn: AsyncConnection>(
940 self,
941 connection: &Cn,
942 ) -> Result<CollectionDocument<Self>, InsertError<Self>>
943 where
944 Self: SerializedCollection<Contents = Self> + Sized + 'static,
945 {
946 Self::push_async(self, connection).await
947 }
948
949 /// Pushes an insert [`Operation`] without a key to the transaction for this
950 /// document, allowing the database to generate the primary key for the
951 /// document.
952 ///
953 /// The document will be inserted once the transaction is applied.
954 fn push_in_transaction(&self, transaction: &mut Transaction) -> Result<(), Error>
955 where
956 Self: SerializedCollection<Contents = Self> + Sized + 'static,
957 {
958 transaction.push(Operation::push_serialized::<Self>(self)?);
959 Ok(())
960 }
961
962 /// Inserts this value into the collection with the specified id, returning
963 /// the created document.
964 ///
965 /// ```rust
966 /// # bonsaidb_core::__doctest_prelude!();
967 /// # use bonsaidb_core::connection::Connection;
968 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
969 /// let document = MyCollection::insert(&42, MyCollection::default(), &db)?;
970 /// assert_eq!(document.header.id, 42);
971 /// println!(
972 /// "Inserted {:?} with revision {}",
973 /// document.contents, document.header.revision
974 /// );
975 /// # Ok(())
976 /// # }
977 /// ```
978 fn insert<PrimaryKey, Cn>(
979 id: &PrimaryKey,
980 contents: Self::Contents,
981 connection: &Cn,
982 ) -> Result<CollectionDocument<Self>, InsertError<Self::Contents>>
983 where
984 PrimaryKey: KeyEncoding<Self::PrimaryKey>,
985 Cn: Connection,
986 Self: Sized + 'static,
987 {
988 let header = match connection.collection::<Self>().insert(id, &contents) {
989 Ok(header) => header,
990 Err(error) => return Err(InsertError { contents, error }),
991 };
992 Ok(CollectionDocument { header, contents })
993 }
994
995 /// Inserts this value into the collection with the specified id, returning
996 /// the created document.
997 ///
998 /// ```rust
999 /// # bonsaidb_core::__doctest_prelude!();
1000 /// # use bonsaidb_core::connection::AsyncConnection;
1001 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
1002 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1003 /// let document = MyCollection::insert_async(&42, MyCollection::default(), &db).await?;
1004 /// assert_eq!(document.header.id, 42);
1005 /// println!(
1006 /// "Inserted {:?} with revision {}",
1007 /// document.contents, document.header.revision
1008 /// );
1009 /// # Ok(())
1010 /// # })
1011 /// # }
1012 /// ```
1013 async fn insert_async<PrimaryKey, Cn>(
1014 id: &PrimaryKey,
1015 contents: Self::Contents,
1016 connection: &Cn,
1017 ) -> Result<CollectionDocument<Self>, InsertError<Self::Contents>>
1018 where
1019 PrimaryKey: KeyEncoding<Self::PrimaryKey>,
1020 Cn: AsyncConnection,
1021 Self: Sized + 'static,
1022 Self::Contents: 'async_trait,
1023 {
1024 let header = match connection.collection::<Self>().insert(id, &contents).await {
1025 Ok(header) => header,
1026 Err(error) => return Err(InsertError { contents, error }),
1027 };
1028 Ok(CollectionDocument { header, contents })
1029 }
1030
1031 /// Inserts this value into the collection with the given `id`, returning
1032 /// the created document.
1033 ///
1034 /// ```rust
1035 /// # bonsaidb_core::__doctest_prelude!();
1036 /// # use bonsaidb_core::connection::Connection;
1037 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
1038 /// let document = MyCollection::default().insert_into(&42, &db)?;
1039 /// assert_eq!(document.header.id, 42);
1040 /// println!(
1041 /// "Inserted {:?} with revision {}",
1042 /// document.contents, document.header.revision
1043 /// );
1044 /// # Ok(())
1045 /// # }
1046 /// ```
1047 fn insert_into<PrimaryKey, Cn>(
1048 self,
1049 id: &PrimaryKey,
1050 connection: &Cn,
1051 ) -> Result<CollectionDocument<Self>, InsertError<Self>>
1052 where
1053 PrimaryKey: KeyEncoding<Self::PrimaryKey>,
1054 Cn: Connection,
1055 Self: SerializedCollection<Contents = Self> + Sized + 'static,
1056 {
1057 Self::insert(id, self, connection)
1058 }
1059
1060 /// Inserts this value into the collection with the given `id`, returning
1061 /// the created document.
1062 ///
1063 /// ```rust
1064 /// # bonsaidb_core::__doctest_prelude!();
1065 /// # use bonsaidb_core::connection::AsyncConnection;
1066 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
1067 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1068 /// let document = MyCollection::default().insert_into_async(&42, &db).await?;
1069 /// assert_eq!(document.header.id, 42);
1070 /// println!(
1071 /// "Inserted {:?} with revision {}",
1072 /// document.contents, document.header.revision
1073 /// );
1074 /// # Ok(())
1075 /// # })
1076 /// # }
1077 /// ```
1078 async fn insert_into_async<PrimaryKey, Cn>(
1079 self,
1080 id: &PrimaryKey,
1081 connection: &Cn,
1082 ) -> Result<CollectionDocument<Self>, InsertError<Self>>
1083 where
1084 PrimaryKey: KeyEncoding<Self::PrimaryKey>,
1085 Cn: AsyncConnection,
1086 Self: SerializedCollection<Contents = Self> + Sized + 'static,
1087 {
1088 Self::insert_async(id, self, connection).await
1089 }
1090
1091 /// Pushes an insert [`Operation`] to the transaction for this document.
1092 ///
1093 /// The document will be inserted once the transaction is applied.
1094 fn insert_in_transaction(
1095 &self,
1096 key: &Self::PrimaryKey,
1097 transaction: &mut Transaction,
1098 ) -> Result<(), Error>
1099 where
1100 Self: SerializedCollection<Contents = Self> + Sized + 'static,
1101 {
1102 transaction.push(Operation::insert_serialized::<Self>(Some(key), self)?);
1103 Ok(())
1104 }
1105
1106 /// Overwrites this value into the collection with the specified id, returning
1107 /// the created or updated document.
1108 ///
1109 /// ```rust
1110 /// # bonsaidb_core::__doctest_prelude!();
1111 /// # use bonsaidb_core::connection::Connection;
1112 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
1113 /// let document = MyCollection::overwrite(&42, MyCollection::default(), &db)?;
1114 /// assert_eq!(document.header.id, 42);
1115 /// println!(
1116 /// "Overwrote {:?} with revision {}",
1117 /// document.contents, document.header.revision
1118 /// );
1119 /// # Ok(())
1120 /// # }
1121 /// ```
1122 fn overwrite<PrimaryKey, Cn>(
1123 id: &PrimaryKey,
1124 contents: Self::Contents,
1125 connection: &Cn,
1126 ) -> Result<CollectionDocument<Self>, InsertError<Self::Contents>>
1127 where
1128 PrimaryKey: KeyEncoding<Self::PrimaryKey>,
1129 Cn: Connection,
1130 Self: Sized + 'static,
1131 {
1132 let header = match Self::serialize(&contents) {
1133 Ok(serialized) => match connection.overwrite::<Self, _>(id, serialized) {
1134 Ok(header) => header,
1135 Err(error) => return Err(InsertError { contents, error }),
1136 },
1137 Err(error) => return Err(InsertError { contents, error }),
1138 };
1139 Ok(CollectionDocument { header, contents })
1140 }
1141
1142 /// Overwrites this value into the collection with the specified id, returning
1143 /// the created or updated document.
1144 ///
1145 /// ```rust
1146 /// # bonsaidb_core::__doctest_prelude!();
1147 /// # use bonsaidb_core::connection::AsyncConnection;
1148 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
1149 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1150 /// let document = MyCollection::overwrite_async(&42, MyCollection::default(), &db).await?;
1151 /// assert_eq!(document.header.id, 42);
1152 /// println!(
1153 /// "Overwrote {:?} with revision {}",
1154 /// document.contents, document.header.revision
1155 /// );
1156 /// # Ok(())
1157 /// # })
1158 /// # }
1159 /// ```
1160 async fn overwrite_async<PrimaryKey, Cn>(
1161 id: &PrimaryKey,
1162 contents: Self::Contents,
1163 connection: &Cn,
1164 ) -> Result<CollectionDocument<Self>, InsertError<Self::Contents>>
1165 where
1166 PrimaryKey: KeyEncoding<Self::PrimaryKey>,
1167 Cn: AsyncConnection,
1168 Self: Sized + 'static,
1169 Self::Contents: 'async_trait,
1170 {
1171 let header = match Self::serialize(&contents) {
1172 Ok(serialized) => match connection.overwrite::<Self, _>(id, serialized).await {
1173 Ok(header) => header,
1174 Err(error) => return Err(InsertError { contents, error }),
1175 },
1176 Err(error) => return Err(InsertError { contents, error }),
1177 };
1178 Ok(CollectionDocument { header, contents })
1179 }
1180
1181 /// Overwrites this value into the collection with the given `id`, returning
1182 /// the created or updated document.
1183 ///
1184 /// ```rust
1185 /// # bonsaidb_core::__doctest_prelude!();
1186 /// # use bonsaidb_core::connection::Connection;
1187 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
1188 /// let document = MyCollection::default().overwrite_into(&42, &db)?;
1189 /// assert_eq!(document.header.id, 42);
1190 /// println!(
1191 /// "Overwrote {:?} with revision {}",
1192 /// document.contents, document.header.revision
1193 /// );
1194 /// # Ok(())
1195 /// # }
1196 /// ```
1197 fn overwrite_into<Cn: Connection, PrimaryKey>(
1198 self,
1199 id: &PrimaryKey,
1200 connection: &Cn,
1201 ) -> Result<CollectionDocument<Self>, InsertError<Self>>
1202 where
1203 PrimaryKey: KeyEncoding<Self::PrimaryKey>,
1204 Self: SerializedCollection<Contents = Self> + Sized + 'static,
1205 {
1206 Self::overwrite(id, self, connection)
1207 }
1208
1209 /// Pushes an overwrite [`Operation`] to the transaction for this document.
1210 ///
1211 /// The document will be overwritten once the transaction is applied.
1212 fn overwrite_in_transaction<PrimaryKey>(
1213 &self,
1214 id: &PrimaryKey,
1215 transaction: &mut Transaction,
1216 ) -> Result<(), Error>
1217 where
1218 PrimaryKey: KeyEncoding<Self::PrimaryKey>,
1219 Self: SerializedCollection<Contents = Self> + Sized + 'static,
1220 {
1221 transaction.push(Operation::overwrite_serialized::<Self, PrimaryKey>(
1222 id, self,
1223 )?);
1224 Ok(())
1225 }
1226
1227 /// Overwrites this value into the collection with the given `id`, returning
1228 /// the created or updated document.
1229 ///
1230 /// ```rust
1231 /// # bonsaidb_core::__doctest_prelude!();
1232 /// # use bonsaidb_core::connection::AsyncConnection;
1233 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
1234 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1235 /// let document = MyCollection::default()
1236 /// .overwrite_into_async(&42, &db)
1237 /// .await?;
1238 /// assert_eq!(document.header.id, 42);
1239 /// println!(
1240 /// "Overwrote {:?} with revision {}",
1241 /// document.contents, document.header.revision
1242 /// );
1243 /// # Ok(())
1244 /// # })
1245 /// # }
1246 /// ```
1247 async fn overwrite_into_async<Cn: AsyncConnection, PrimaryKey>(
1248 self,
1249 id: &PrimaryKey,
1250 connection: &Cn,
1251 ) -> Result<CollectionDocument<Self>, InsertError<Self>>
1252 where
1253 PrimaryKey: KeyEncoding<Self::PrimaryKey>,
1254 Self: SerializedCollection<Contents = Self> + Sized + 'static,
1255 {
1256 Self::overwrite_async(id, self, connection).await
1257 }
1258}
1259
1260/// A convenience trait for easily storing Serde-compatible types in documents.
1261pub trait DefaultSerialization: Collection {
1262 /// Returns the natural identifier of `contents`. This is called when
1263 /// pushing values into a collection, before attempting to automatically
1264 /// assign a unique id.
1265 fn natural_id(&self) -> Option<Self::PrimaryKey> {
1266 None
1267 }
1268}
1269
1270impl<T> SerializedCollection for T
1271where
1272 T: DefaultSerialization + Serialize + DeserializeOwned,
1273{
1274 type Contents = Self;
1275 type Format = Pot;
1276
1277 fn format() -> Self::Format {
1278 Pot::default()
1279 }
1280
1281 fn natural_id(contents: &Self::Contents) -> Option<Self::PrimaryKey> {
1282 T::natural_id(contents)
1283 }
1284}
1285
1286/// An error from inserting a [`CollectionDocument`].
1287#[derive(thiserror::Error, Debug)]
1288#[error("{error}")]
1289pub struct InsertError<T> {
1290 /// The original value being inserted.
1291 pub contents: T,
1292 /// The error that occurred while inserting.
1293 pub error: Error,
1294}
1295
1296/// A collection with a unique name column.
1297///
1298/// ## Finding a document by unique name
1299///
1300/// ```rust
1301/// # bonsaidb_core::__doctest_prelude!();
1302/// # use bonsaidb_core::connection::Connection;
1303/// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
1304/// if let Some(doc) = MyCollection::load("unique name", &db)? {
1305/// println!(
1306/// "Retrieved revision {} with deserialized contents: {:?}",
1307/// doc.header.revision, doc.contents
1308/// );
1309/// }
1310/// # Ok(())
1311/// # }
1312/// ```
1313///
1314/// Load accepts either a string or a [`DocumentId`]. This enables building
1315/// methods that accept either the unique ID or the unique name:
1316///
1317/// ```rust
1318/// # bonsaidb_core::__doctest_prelude!();
1319/// # use bonsaidb_core::connection::Connection;
1320/// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
1321/// if let Some(doc) = MyCollection::load(42, &db)? {
1322/// println!(
1323/// "Retrieved revision {} with deserialized contents: {:?}",
1324/// doc.header.revision, doc.contents
1325/// );
1326/// }
1327/// # Ok(())
1328/// # }
1329/// ```
1330///
1331/// ## Executing an insert or update
1332///
1333/// ```rust
1334/// # bonsaidb_core::__doctest_prelude!();
1335/// # use bonsaidb_core::connection::Connection;
1336/// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
1337/// let upserted = MyCollection::entry("unique name", &db)
1338/// .update_with(|existing: &mut MyCollection| {
1339/// existing.rank += 1;
1340/// })
1341/// .or_insert_with(MyCollection::default)
1342/// .execute()?
1343/// .unwrap();
1344/// println!("Rank: {:?}", upserted.contents.rank);
1345///
1346/// # Ok(())
1347/// # }
1348/// ```
1349#[async_trait]
1350pub trait NamedCollection: Collection + Unpin {
1351 /// The name view defined for the collection.
1352 type ByNameView: crate::schema::SerializedView<Key = String, Collection = Self>;
1353
1354 /// Gets a [`CollectionDocument`] with `id` from `connection`.
1355 fn load<'name, N: Nameable<'name, Self::PrimaryKey> + Send + Sync, C: Connection>(
1356 id: N,
1357 connection: &C,
1358 ) -> Result<Option<CollectionDocument<Self>>, Error>
1359 where
1360 Self: SerializedCollection + Sized + 'static,
1361 {
1362 let possible_doc = Self::load_document(id, connection)?;
1363 possible_doc
1364 .as_ref()
1365 .map(CollectionDocument::try_from)
1366 .transpose()
1367 }
1368
1369 /// Gets a [`CollectionDocument`] with `id` from `connection`.
1370 async fn load_async<
1371 'name,
1372 N: Nameable<'name, Self::PrimaryKey> + Send + Sync,
1373 C: AsyncConnection,
1374 >(
1375 id: N,
1376 connection: &C,
1377 ) -> Result<Option<CollectionDocument<Self>>, Error>
1378 where
1379 Self: SerializedCollection + Sized + 'static,
1380 {
1381 let possible_doc = Self::load_document_async(id, connection).await?;
1382 Ok(possible_doc
1383 .as_ref()
1384 .map(CollectionDocument::try_from)
1385 .transpose()?)
1386 }
1387
1388 /// Gets a [`CollectionDocument`] with `id` from `connection`.
1389 fn entry<
1390 'connection,
1391 'name,
1392 N: Into<NamedReference<'name, Self::PrimaryKey>> + Send + Sync,
1393 C: Connection,
1394 >(
1395 id: N,
1396 connection: &'connection C,
1397 ) -> Entry<'connection, 'name, C, Self, (), ()>
1398 where
1399 Self: SerializedCollection + Sized,
1400 {
1401 let name = id.into();
1402 Entry {
1403 name,
1404 connection,
1405 insert: None,
1406 update: None,
1407 retry_limit: 0,
1408 _collection: PhantomData,
1409 }
1410 }
1411
1412 /// Gets a [`CollectionDocument`] with `id` from `connection`.
1413 fn entry_async<
1414 'connection,
1415 'name,
1416 N: Into<NamedReference<'name, Self::PrimaryKey>> + Send + Sync,
1417 C: AsyncConnection,
1418 >(
1419 id: N,
1420 connection: &'connection C,
1421 ) -> AsyncEntry<'connection, 'name, C, Self, (), ()>
1422 where
1423 Self: SerializedCollection + Sized,
1424 {
1425 let name = id.into();
1426 AsyncEntry {
1427 state: EntryState::Pending(Some(EntryBuilder {
1428 name,
1429 connection,
1430 insert: None,
1431 update: None,
1432 retry_limit: 0,
1433 _collection: PhantomData,
1434 })),
1435 }
1436 }
1437
1438 /// Loads a document from this collection by name, if applicable. Return
1439 /// `Ok(None)` if unsupported.
1440 fn load_document<'name, N: Nameable<'name, Self::PrimaryKey> + Send + Sync, C: Connection>(
1441 name: N,
1442 connection: &C,
1443 ) -> Result<Option<OwnedDocument>, Error>
1444 where
1445 Self: SerializedCollection + Sized,
1446 {
1447 match name.name()? {
1448 NamedReference::Id(id) => connection.collection::<Self>().get(&id),
1449 NamedReference::Key(id) => connection.collection::<Self>().get(&id),
1450 NamedReference::Name(name) => Ok(connection
1451 .view::<Self::ByNameView>()
1452 .with_key(name.as_ref())
1453 .query_with_docs()?
1454 .documents
1455 .into_iter()
1456 .next()
1457 .map(|(_, document)| document)),
1458 }
1459 }
1460
1461 /// Loads a document from this collection by name, if applicable. Return
1462 /// `Ok(None)` if unsupported.
1463 async fn load_document_async<
1464 'name,
1465 N: Nameable<'name, Self::PrimaryKey> + Send + Sync,
1466 C: AsyncConnection,
1467 >(
1468 name: N,
1469 connection: &C,
1470 ) -> Result<Option<OwnedDocument>, Error>
1471 where
1472 Self: SerializedCollection + Sized,
1473 {
1474 match name.name()? {
1475 NamedReference::Id(id) => connection.collection::<Self>().get(&id).await,
1476 NamedReference::Key(id) => connection.collection::<Self>().get(&id).await,
1477 NamedReference::Name(name) => Ok(connection
1478 .view::<Self::ByNameView>()
1479 .with_key(name.as_ref())
1480 .query_with_docs()
1481 .await?
1482 .documents
1483 .into_iter()
1484 .next()
1485 .map(|(_, document)| document)),
1486 }
1487 }
1488
1489 /// Deletes a document by its name. Returns true if a document was deleted.
1490 fn delete_by_name<C: Connection>(name: &str, connection: &C) -> Result<bool, Error>
1491 where
1492 Self: SerializedCollection + Sized,
1493 {
1494 Ok(connection
1495 .view::<Self::ByNameView>()
1496 .with_key(name)
1497 .delete_docs()?
1498 > 0)
1499 }
1500
1501 /// Deletes a document by its name. Returns true if a document was deleted.
1502 async fn delete_by_name_async<C: AsyncConnection>(
1503 name: &str,
1504 connection: &C,
1505 ) -> Result<bool, Error>
1506 where
1507 Self: SerializedCollection + Sized,
1508 {
1509 Ok(connection
1510 .view::<Self::ByNameView>()
1511 .with_key(name)
1512 .delete_docs()
1513 .await?
1514 > 0)
1515 }
1516}
1517
1518/// A reference to a collection that has a unique name view.
1519#[derive(Clone, Eq, PartialEq, Deserialize, Serialize, Debug)]
1520#[must_use]
1521pub enum NamedReference<'a, Id> {
1522 /// An entity's name.
1523 Name(Cow<'a, str>),
1524 /// A document id.
1525 Id(DocumentId),
1526 /// A document id.
1527 Key(Id),
1528}
1529
1530impl<'a, Id> From<&'a str> for NamedReference<'a, Id> {
1531 fn from(name: &'a str) -> Self {
1532 Self::Name(Cow::Borrowed(name))
1533 }
1534}
1535
1536/// A type that can be used as a unique reference for a collection that
1537/// implements [`NamedCollection`].
1538pub trait Nameable<'a, Id> {
1539 /// Returns this name as a [`NamedReference`].
1540 fn name(self) -> Result<NamedReference<'a, Id>, crate::Error>;
1541}
1542
1543impl<'a, Id> Nameable<'a, Id> for NamedReference<'a, Id> {
1544 fn name(self) -> Result<NamedReference<'a, Id>, crate::Error> {
1545 Ok(self)
1546 }
1547}
1548
1549impl<'a, Id> Nameable<'a, Id> for &'a NamedReference<'a, Id>
1550where
1551 Id: Clone,
1552{
1553 fn name(self) -> Result<NamedReference<'a, Id>, crate::Error> {
1554 Ok(match self {
1555 NamedReference::Name(name) => NamedReference::Name(name.clone()),
1556 NamedReference::Id(id) => NamedReference::Id(id.clone()),
1557 NamedReference::Key(key) => NamedReference::Key(key.clone()),
1558 })
1559 }
1560}
1561
1562impl<'a, Id> Nameable<'a, Id> for &'a str {
1563 fn name(self) -> Result<NamedReference<'a, Id>, crate::Error> {
1564 Ok(NamedReference::from(self))
1565 }
1566}
1567
1568impl<'a, Id> From<&'a String> for NamedReference<'a, Id> {
1569 fn from(name: &'a String) -> Self {
1570 Self::Name(Cow::Borrowed(name.as_str()))
1571 }
1572}
1573
1574impl<'a, Id> Nameable<'a, Id> for &'a String {
1575 fn name(self) -> Result<NamedReference<'a, Id>, crate::Error> {
1576 Ok(NamedReference::from(self))
1577 }
1578}
1579
1580impl<'a, 'b, Id> From<&'b BorrowedDocument<'b>> for NamedReference<'a, Id> {
1581 fn from(doc: &'b BorrowedDocument<'b>) -> Self {
1582 Self::Id(doc.header.id.clone())
1583 }
1584}
1585
1586impl<'a, 'b, Id> Nameable<'a, Id> for &'a BorrowedDocument<'b> {
1587 fn name(self) -> Result<NamedReference<'a, Id>, crate::Error> {
1588 Ok(NamedReference::from(self))
1589 }
1590}
1591
1592impl<'a, 'c, C> TryFrom<&'c CollectionDocument<C>> for NamedReference<'a, C::PrimaryKey>
1593where
1594 C: SerializedCollection,
1595{
1596 type Error = crate::Error;
1597
1598 fn try_from(doc: &'c CollectionDocument<C>) -> Result<Self, crate::Error> {
1599 DocumentId::new(&doc.header.id).map(Self::Id)
1600 }
1601}
1602
1603impl<'a, C> Nameable<'a, C::PrimaryKey> for &'a CollectionDocument<C>
1604where
1605 C: SerializedCollection,
1606{
1607 fn name(self) -> Result<NamedReference<'a, C::PrimaryKey>, crate::Error> {
1608 NamedReference::try_from(self)
1609 }
1610}
1611
1612impl<'a, Id> From<String> for NamedReference<'a, Id> {
1613 fn from(name: String) -> Self {
1614 Self::Name(Cow::Owned(name))
1615 }
1616}
1617
1618impl<'a, Id> Nameable<'a, Id> for String {
1619 fn name(self) -> Result<NamedReference<'a, Id>, crate::Error> {
1620 Ok(NamedReference::from(self))
1621 }
1622}
1623
1624impl<'a, Id> From<DocumentId> for NamedReference<'a, Id> {
1625 fn from(id: DocumentId) -> Self {
1626 Self::Id(id)
1627 }
1628}
1629
1630impl<'a, Id> Nameable<'a, Id> for DocumentId {
1631 fn name(self) -> Result<NamedReference<'a, Id>, crate::Error> {
1632 Ok(NamedReference::from(self))
1633 }
1634}
1635
1636impl<'a> Nameable<'a, Self> for u64 {
1637 fn name(self) -> Result<NamedReference<'a, Self>, crate::Error> {
1638 Ok(NamedReference::Key(self))
1639 }
1640}
1641
1642impl<'a, Id> NamedReference<'a, Id>
1643where
1644 Id: for<'k> Key<'k>,
1645{
1646 /// Converts this reference to an owned reference with a `'static` lifetime.
1647 pub fn into_owned(self) -> NamedReference<'static, Id> {
1648 match self {
1649 Self::Name(name) => NamedReference::Name(match name {
1650 Cow::Owned(string) => Cow::Owned(string),
1651 Cow::Borrowed(borrowed) => Cow::Owned(borrowed.to_owned()),
1652 }),
1653 Self::Id(id) => NamedReference::Id(id),
1654 Self::Key(key) => NamedReference::Key(key),
1655 }
1656 }
1657
1658 /// Returns this reference's id. If the reference is a name, the
1659 /// [`NamedCollection::ByNameView`] is queried for the id.
1660 pub fn id<Col: NamedCollection<PrimaryKey = Id>, Cn: Connection>(
1661 &self,
1662 connection: &Cn,
1663 ) -> Result<Option<Col::PrimaryKey>, Error> {
1664 match self {
1665 Self::Name(name) => connection
1666 .view::<Col::ByNameView>()
1667 .with_key(name.as_ref())
1668 .query()?
1669 .into_iter()
1670 .next()
1671 .map(|e| Ok(e.source.id))
1672 .transpose(),
1673 Self::Id(id) => Ok(Some(id.deserialize()?)),
1674 Self::Key(id) => Ok(Some(id.clone())),
1675 }
1676 }
1677
1678 /// Returns this reference's id. If the reference is a name, the
1679 /// [`NamedCollection::ByNameView`] is queried for the id.
1680 pub async fn id_async<Col: NamedCollection<PrimaryKey = Id>, Cn: AsyncConnection>(
1681 &self,
1682 connection: &Cn,
1683 ) -> Result<Option<Col::PrimaryKey>, Error> {
1684 match self {
1685 Self::Name(name) => connection
1686 .view::<Col::ByNameView>()
1687 .with_key(name.as_ref())
1688 .query()
1689 .await?
1690 .into_iter()
1691 .next()
1692 .map(|e| Ok(e.source.id))
1693 .transpose(),
1694 Self::Id(id) => Ok(Some(id.deserialize()?)),
1695 Self::Key(id) => Ok(Some(id.clone())),
1696 }
1697 }
1698}
1699
1700/// A future that resolves to an entry in a [`NamedCollection`].
1701#[must_use]
1702pub struct Entry<'a, 'name, Connection, Col, EI, EU>
1703where
1704 Col: NamedCollection + SerializedCollection,
1705 EI: EntryInsert<Col>,
1706 EU: EntryUpdate<Col>,
1707{
1708 name: NamedReference<'name, Col::PrimaryKey>,
1709 connection: &'a Connection,
1710 insert: Option<EI>,
1711 update: Option<EU>,
1712 retry_limit: usize,
1713 _collection: PhantomData<Col>,
1714}
1715
1716impl<'a, 'name, Connection, Col, EI, EU> Entry<'a, 'name, Connection, Col, EI, EU>
1717where
1718 Col: NamedCollection + SerializedCollection + 'static + Unpin,
1719 Connection: crate::connection::Connection,
1720 EI: EntryInsert<Col> + 'a + Unpin,
1721 EU: EntryUpdate<Col> + 'a + Unpin,
1722 'name: 'a,
1723{
1724 pub fn execute(self) -> Result<Option<CollectionDocument<Col>>, Error> {
1725 let Self {
1726 name,
1727 connection,
1728 insert,
1729 update,
1730 mut retry_limit,
1731 ..
1732 } = self;
1733 if let Some(mut existing) = Col::load(name, connection)? {
1734 if let Some(update) = update {
1735 loop {
1736 update.call(&mut existing.contents);
1737 match existing.update(connection) {
1738 Ok(()) => return Ok(Some(existing)),
1739 Err(Error::DocumentConflict(collection, header)) => {
1740 // Another client has updated the document underneath us.
1741 if retry_limit > 0 {
1742 retry_limit -= 1;
1743 existing = match Col::load(header.id, connection)? {
1744 Some(doc) => doc,
1745 // Another client deleted the document before we could reload it.
1746 None => break Ok(None),
1747 }
1748 } else {
1749 break Err(Error::DocumentConflict(collection, header));
1750 }
1751 }
1752 Err(other) => break Err(other),
1753 }
1754 }
1755 } else {
1756 Ok(Some(existing))
1757 }
1758 } else if let Some(insert) = insert {
1759 let new_document = insert.call();
1760 Ok(Some(Col::push(new_document, connection)?))
1761 } else {
1762 Ok(None)
1763 }
1764 }
1765
1766 /// If an entry with the key doesn't exist, `cb` will be executed to provide
1767 /// an initial document. This document will be saved before being returned.
1768 #[allow(clippy::missing_const_for_fn)] // false positive, destructors
1769 pub fn or_insert_with<F: EntryInsert<Col> + 'a + Unpin>(
1770 self,
1771 cb: F,
1772 ) -> Entry<'a, 'name, Connection, Col, F, EU> {
1773 Entry {
1774 name: self.name,
1775 connection: self.connection,
1776 insert: Some(cb),
1777 update: self.update,
1778 retry_limit: self.retry_limit,
1779 _collection: PhantomData,
1780 }
1781 }
1782
1783 /// If an entry with the keys exists, `cb` will be executed with the stored
1784 /// value, allowing an opportunity to update the value. This new value will
1785 /// be saved to the database before returning. If an error occurs during
1786 /// update, `cb` may be invoked multiple times, up to the
1787 /// [`retry_limit`](Self::retry_limit()).
1788 #[allow(clippy::missing_const_for_fn)] // false positive, destructors
1789 pub fn update_with<F: EntryUpdate<Col> + 'a + Unpin>(
1790 self,
1791 cb: F,
1792 ) -> Entry<'a, 'name, Connection, Col, EI, F> {
1793 Entry {
1794 name: self.name,
1795 connection: self.connection,
1796 update: Some(cb),
1797 insert: self.insert,
1798 retry_limit: self.retry_limit,
1799 _collection: PhantomData,
1800 }
1801 }
1802
1803 /// The number of attempts to attempt updating the document using
1804 /// `update_with` before returning an error.
1805 pub const fn retry_limit(mut self, attempts: usize) -> Self {
1806 self.retry_limit = attempts;
1807 self
1808 }
1809}
1810
1811/// A future that resolves to an entry in a [`NamedCollection`].
1812#[must_use]
1813pub struct AsyncEntry<'a, 'name, Connection, Col, EI, EU>
1814where
1815 Col: NamedCollection + SerializedCollection,
1816 EI: EntryInsert<Col>,
1817 EU: EntryUpdate<Col>,
1818{
1819 state: EntryState<'a, 'name, Connection, Col, EI, EU>,
1820}
1821
1822struct EntryBuilder<
1823 'a,
1824 'name,
1825 Connection,
1826 Col,
1827 EI: EntryInsert<Col> + 'a,
1828 EU: EntryUpdate<Col> + 'a,
1829> where
1830 Col: SerializedCollection,
1831{
1832 name: NamedReference<'name, Col::PrimaryKey>,
1833 connection: &'a Connection,
1834 insert: Option<EI>,
1835 update: Option<EU>,
1836 retry_limit: usize,
1837 _collection: PhantomData<Col>,
1838}
1839
1840impl<'a, 'name, Connection, Col, EI, EU> AsyncEntry<'a, 'name, Connection, Col, EI, EU>
1841where
1842 Col: NamedCollection + SerializedCollection + 'static + Unpin,
1843 Connection: crate::connection::AsyncConnection,
1844 EI: EntryInsert<Col> + 'a + Unpin,
1845 EU: EntryUpdate<Col> + 'a + Unpin,
1846 'name: 'a,
1847{
1848 async fn execute(
1849 name: NamedReference<'name, Col::PrimaryKey>,
1850 connection: &'a Connection,
1851 insert: Option<EI>,
1852 update: Option<EU>,
1853 mut retry_limit: usize,
1854 ) -> Result<Option<CollectionDocument<Col>>, Error> {
1855 if let Some(mut existing) = Col::load_async(name, connection).await? {
1856 if let Some(update) = update {
1857 loop {
1858 update.call(&mut existing.contents);
1859 match existing.update_async(connection).await {
1860 Ok(()) => return Ok(Some(existing)),
1861 Err(Error::DocumentConflict(collection, header)) => {
1862 // Another client has updated the document underneath us.
1863 if retry_limit > 0 {
1864 retry_limit -= 1;
1865 existing = match Col::load_async(header.id, connection).await? {
1866 Some(doc) => doc,
1867 // Another client deleted the document before we could reload it.
1868 None => break Ok(None),
1869 }
1870 } else {
1871 break Err(Error::DocumentConflict(collection, header));
1872 }
1873 }
1874 Err(other) => break Err(other),
1875 }
1876 }
1877 } else {
1878 Ok(Some(existing))
1879 }
1880 } else if let Some(insert) = insert {
1881 let new_document = insert.call();
1882 Ok(Some(Col::push_async(new_document, connection).await?))
1883 } else {
1884 Ok(None)
1885 }
1886 }
1887
1888 fn pending(&mut self) -> &mut EntryBuilder<'a, 'name, Connection, Col, EI, EU> {
1889 match &mut self.state {
1890 EntryState::Pending(pending) => pending.as_mut().unwrap(),
1891 EntryState::Executing(_) => unreachable!(),
1892 }
1893 }
1894
1895 /// If an entry with the key doesn't exist, `cb` will be executed to provide
1896 /// an initial document. This document will be saved before being returned.
1897 pub fn or_insert_with<F: EntryInsert<Col> + 'a + Unpin>(
1898 self,
1899 cb: F,
1900 ) -> AsyncEntry<'a, 'name, Connection, Col, F, EU> {
1901 AsyncEntry {
1902 state: match self.state {
1903 EntryState::Pending(Some(EntryBuilder {
1904 name,
1905 connection,
1906 update,
1907 retry_limit,
1908 ..
1909 })) => EntryState::Pending(Some(EntryBuilder {
1910 name,
1911 connection,
1912 insert: Some(cb),
1913 update,
1914 retry_limit,
1915 _collection: PhantomData,
1916 })),
1917 _ => {
1918 unreachable!("attempting to modify an already executing future")
1919 }
1920 },
1921 }
1922 }
1923
1924 /// If an entry with the keys exists, `cb` will be executed with the stored
1925 /// value, allowing an opportunity to update the value. This new value will
1926 /// be saved to the database before returning. If an error occurs during
1927 /// update, `cb` may be invoked multiple times, up to the
1928 /// [`retry_limit`](Self::retry_limit()).
1929 pub fn update_with<F: EntryUpdate<Col> + 'a + Unpin>(
1930 self,
1931 cb: F,
1932 ) -> AsyncEntry<'a, 'name, Connection, Col, EI, F> {
1933 AsyncEntry {
1934 state: match self.state {
1935 EntryState::Pending(Some(EntryBuilder {
1936 name,
1937 connection,
1938 insert,
1939 retry_limit,
1940 ..
1941 })) => EntryState::Pending(Some(EntryBuilder {
1942 name,
1943 connection,
1944 insert,
1945 update: Some(cb),
1946 retry_limit,
1947 _collection: PhantomData,
1948 })),
1949 _ => {
1950 unreachable!("attempting to modify an already executing future")
1951 }
1952 },
1953 }
1954 }
1955
1956 /// The number of attempts to attempt updating the document using
1957 /// `update_with` before returning an error.
1958 pub fn retry_limit(mut self, attempts: usize) -> Self {
1959 self.pending().retry_limit = attempts;
1960 self
1961 }
1962}
1963
1964pub trait EntryInsert<Col: SerializedCollection>: Send + Unpin {
1965 fn call(self) -> Col::Contents;
1966}
1967
1968impl<F, Col> EntryInsert<Col> for F
1969where
1970 F: FnOnce() -> Col::Contents + Send + Unpin,
1971 Col: SerializedCollection,
1972{
1973 fn call(self) -> Col::Contents {
1974 self()
1975 }
1976}
1977
1978impl<Col> EntryInsert<Col> for ()
1979where
1980 Col: SerializedCollection,
1981{
1982 fn call(self) -> Col::Contents {
1983 unreachable!()
1984 }
1985}
1986
1987pub trait EntryUpdate<Col>: Send + Unpin
1988where
1989 Col: SerializedCollection,
1990{
1991 fn call(&self, doc: &mut Col::Contents);
1992}
1993
1994impl<F, Col> EntryUpdate<Col> for F
1995where
1996 F: Fn(&mut Col::Contents) + Send + Unpin,
1997 Col: NamedCollection + SerializedCollection,
1998{
1999 fn call(&self, doc: &mut Col::Contents) {
2000 self(doc);
2001 }
2002}
2003
2004impl<Col> EntryUpdate<Col> for ()
2005where
2006 Col: SerializedCollection,
2007{
2008 fn call(&self, _doc: &mut Col::Contents) {
2009 unreachable!();
2010 }
2011}
2012
2013impl<'a, 'name, Conn, Col, EI, EU> Future for AsyncEntry<'a, 'name, Conn, Col, EI, EU>
2014where
2015 Col: NamedCollection + SerializedCollection + 'static,
2016 <Col as Collection>::PrimaryKey: Unpin,
2017 Conn: AsyncConnection,
2018 EI: EntryInsert<Col> + 'a,
2019 EU: EntryUpdate<Col> + 'a,
2020 'name: 'a,
2021{
2022 type Output = Result<Option<CollectionDocument<Col>>, Error>;
2023
2024 fn poll(
2025 mut self: std::pin::Pin<&mut Self>,
2026 cx: &mut std::task::Context<'_>,
2027 ) -> Poll<Self::Output> {
2028 if let Some(EntryBuilder {
2029 name,
2030 connection,
2031 insert,
2032 update,
2033 retry_limit,
2034 ..
2035 }) = match &mut self.state {
2036 EntryState::Executing(_) => None,
2037 EntryState::Pending(builder) => builder.take(),
2038 } {
2039 let future = Self::execute(name, connection, insert, update, retry_limit).boxed();
2040 self.state = EntryState::Executing(future);
2041 }
2042
2043 if let EntryState::Executing(future) = &mut self.state {
2044 future.as_mut().poll(cx)
2045 } else {
2046 unreachable!()
2047 }
2048 }
2049}
2050
2051enum EntryState<'a, 'name, Connection, Col, EI, EU>
2052where
2053 Col: NamedCollection + SerializedCollection,
2054 EI: EntryInsert<Col>,
2055 EU: EntryUpdate<Col>,
2056{
2057 Pending(Option<EntryBuilder<'a, 'name, Connection, Col, EI, EU>>),
2058 Executing(BoxFuture<'a, Result<Option<CollectionDocument<Col>>, Error>>),
2059}
2060
2061/// Retrieves a list of documents from a collection. This
2062/// structure also offers functions to customize the options for the operation.
2063#[must_use]
2064pub struct List<'a, Cn, Cl, PrimaryKey>(connection::List<'a, Cn, Cl, PrimaryKey>)
2065where
2066 Cl: Collection,
2067 PrimaryKey: KeyEncoding<Cl::PrimaryKey> + PartialEq + ?Sized,
2068 Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>;
2069
2070impl<'a, Cn, Cl, PrimaryKey> List<'a, Cn, Cl, PrimaryKey>
2071where
2072 Cl: SerializedCollection,
2073 Cn: Connection,
2074 PrimaryKey: KeyEncoding<Cl::PrimaryKey> + PartialEq + ?Sized + 'a,
2075 Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
2076{
2077 /// Lists documents by id in ascending order.
2078 #[allow(clippy::missing_const_for_fn)] // false positive, destructors
2079 pub fn ascending(mut self) -> Self {
2080 self.0 = self.0.ascending();
2081 self
2082 }
2083
2084 /// Lists documents by id in descending order.
2085 #[allow(clippy::missing_const_for_fn)] // false positive, destructors
2086 pub fn descending(mut self) -> Self {
2087 self.0 = self.0.descending();
2088 self
2089 }
2090
2091 /// Sets the maximum number of results to return.
2092 #[allow(clippy::missing_const_for_fn)] // false positive, destructors
2093 pub fn limit(mut self, maximum_results: u32) -> Self {
2094 self.0 = self.0.limit(maximum_results);
2095 self
2096 }
2097
2098 /// Returns the list of document headers contained within the range.
2099 ///
2100 /// ```rust
2101 /// # bonsaidb_core::__doctest_prelude!();
2102 /// # use bonsaidb_core::connection::Connection;
2103 /// # fn test_fn<C: Connection>(db: &C) -> Result<(), Error> {
2104 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
2105 /// println!(
2106 /// "Headers with id 42 or larger: {:?}",
2107 /// MyCollection::list(42.., db).headers()?
2108 /// );
2109 /// println!(
2110 /// "Headers in MyCollection: {:?}",
2111 /// MyCollection::all(db).headers()?
2112 /// );
2113 /// # Ok(())
2114 /// # })
2115 /// # }
2116 /// ```
2117 pub fn headers(self) -> Result<Vec<Header>, Error> {
2118 self.0.headers()
2119 }
2120
2121 /// Returns the number of documents contained within the range.
2122 ///
2123 /// Order and limit are ignored if they were set.
2124 ///
2125 /// ```rust
2126 /// # bonsaidb_core::__doctest_prelude!();
2127 /// # use bonsaidb_core::connection::Connection;
2128 /// # fn test_fn<C: Connection>(db: &C) -> Result<(), Error> {
2129 /// println!(
2130 /// "Number of documents with id 42 or larger: {}",
2131 /// MyCollection::list(42.., db).count()?
2132 /// );
2133 /// println!(
2134 /// "Number of documents in MyCollection: {}",
2135 /// MyCollection::all(db).count()?
2136 /// );
2137 /// # Ok(())
2138 /// # }
2139 /// ```
2140 pub fn count(self) -> Result<u64, Error> {
2141 self.0.count()
2142 }
2143
2144 /// Retrieves the list of documents, using the configured options.
2145 pub fn query(self) -> Result<Vec<CollectionDocument<Cl>>, Error> {
2146 self.0.query().and_then(|docs| docs.collection_documents())
2147 }
2148}
2149
2150/// Retrieves a list of documents from a collection, when awaited. This
2151/// structure also offers functions to customize the options for the operation.
2152#[must_use]
2153pub struct AsyncList<'a, Cn, Cl, PrimaryKey>(connection::AsyncList<'a, Cn, Cl, PrimaryKey>)
2154where
2155 Cl: Collection,
2156 PrimaryKey: KeyEncoding<Cl::PrimaryKey> + PartialEq + ?Sized,
2157 Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>;
2158
2159impl<'a, Cn, Cl, PrimaryKey> AsyncList<'a, Cn, Cl, PrimaryKey>
2160where
2161 Cl: Collection,
2162 Cn: AsyncConnection,
2163 PrimaryKey: KeyEncoding<Cl::PrimaryKey> + PartialEq + ?Sized,
2164 Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
2165{
2166 /// Lists documents by id in ascending order.
2167 pub fn ascending(mut self) -> Self {
2168 self.0 = self.0.ascending();
2169 self
2170 }
2171
2172 /// Lists documents by id in descending order.
2173 pub fn descending(mut self) -> Self {
2174 self.0 = self.0.descending();
2175 self
2176 }
2177
2178 /// Sets the maximum number of results to return.
2179 pub fn limit(mut self, maximum_results: u32) -> Self {
2180 self.0 = self.0.limit(maximum_results);
2181 self
2182 }
2183
2184 /// Returns the number of documents contained within the range.
2185 ///
2186 /// Order and limit are ignored if they were set.
2187 ///
2188 /// ```rust
2189 /// # bonsaidb_core::__doctest_prelude!();
2190 /// # use bonsaidb_core::connection::AsyncConnection;
2191 /// # fn test_fn<C: AsyncConnection>(db: &C) -> Result<(), Error> {
2192 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
2193 /// println!(
2194 /// "Number of documents with id 42 or larger: {}",
2195 /// MyCollection::list_async(42.., db).count().await?
2196 /// );
2197 /// println!(
2198 /// "Number of documents in MyCollection: {}",
2199 /// MyCollection::all_async(db).count().await?
2200 /// );
2201 /// # Ok(())
2202 /// # })
2203 /// # }
2204 /// ```
2205 pub async fn count(self) -> Result<u64, Error> {
2206 self.0.count().await
2207 }
2208
2209 /// Returns the list of document headers contained within the range.
2210 ///
2211 /// ```rust
2212 /// # bonsaidb_core::__doctest_prelude!();
2213 /// # use bonsaidb_core::connection::AsyncConnection;
2214 /// # fn test_fn<C: AsyncConnection>(db: &C) -> Result<(), Error> {
2215 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
2216 /// println!(
2217 /// "Headers with id 42 or larger: {:?}",
2218 /// MyCollection::list_async(42.., db).headers().await?
2219 /// );
2220 /// println!(
2221 /// "Headers in MyCollection: {:?}",
2222 /// MyCollection::all_async(db).headers().await?
2223 /// );
2224 /// # Ok(())
2225 /// # })
2226 /// # }
2227 /// ```
2228 pub async fn headers(self) -> Result<Vec<Header>, Error> {
2229 self.0.headers().await
2230 }
2231}
2232
2233#[allow(clippy::type_repetition_in_bounds)]
2234impl<'a, Cn, Cl, PrimaryKey> Future for AsyncList<'a, Cn, Cl, PrimaryKey>
2235where
2236 Cl: SerializedCollection + Unpin,
2237 Cn: AsyncConnection,
2238 PrimaryKey: KeyEncoding<Cl::PrimaryKey> + PartialEq + Unpin + ?Sized + 'a,
2239 Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey> + Unpin,
2240{
2241 type Output = Result<Vec<CollectionDocument<Cl>>, Error>;
2242
2243 fn poll(
2244 mut self: std::pin::Pin<&mut Self>,
2245 cx: &mut std::task::Context<'_>,
2246 ) -> Poll<Self::Output> {
2247 let result = ready!(self.0.poll_unpin(cx));
2248 Poll::Ready(result.and_then(|docs| docs.collection_documents()))
2249 }
2250}