bonsaidb_core/connection.rs
1use std::borrow::Borrow;
2use std::convert::Infallible;
3use std::marker::PhantomData;
4use std::ops::{Deref, DerefMut};
5use std::string::FromUtf8Error;
6use std::sync::Arc;
7
8use actionable::{Action, Identifier};
9use arc_bytes::serde::Bytes;
10use async_trait::async_trait;
11use futures::future::BoxFuture;
12use futures::{Future, FutureExt};
13use serde::{Deserialize, Serialize};
14use zeroize::Zeroize;
15
16use crate::admin::{Role, User};
17use crate::document::{
18 CollectionDocument, CollectionHeader, Document, HasHeader, Header, OwnedDocument,
19};
20use crate::key::{ByteSource, IntoPrefixRange, Key, KeyEncoding, KeyKind, KeyVisitor};
21use crate::permissions::Permissions;
22use crate::schema::view::map::{MappedDocuments, ViewMappings as ViewMappingsCurrent};
23use crate::schema::{
24 self, MappedValue, Nameable, NamedReference, Schema, SchemaName, SchemaSummary,
25 SerializedCollection,
26};
27use crate::{transaction, Error};
28
29mod has_session;
30mod lowlevel;
31
32pub use self::has_session::HasSession;
33pub use self::lowlevel::{AsyncLowLevelConnection, HasSchema, LowLevelConnection};
34
35/// A connection to a database's [`Schema`](schema::Schema), giving access to
36/// [`Collection`s](crate::schema::Collection) and
37/// [`Views`s](crate::schema::View). This trait is not safe to use within async
38/// contexts and will block the current thread. For async access, use
39/// [`AsyncConnection`].
40pub trait Connection: LowLevelConnection + Sized + Send + Sync {
41 /// The [`StorageConnection`] type that is paired with this type.
42 type Storage: StorageConnection<Database = Self>;
43
44 /// Returns the [`StorageConnection`] implementor that this database belongs to.
45 fn storage(&self) -> Self::Storage;
46
47 /// Accesses a collection for the connected [`Schema`](schema::Schema).
48 fn collection<C: schema::Collection>(&self) -> Collection<'_, Self, C> {
49 Collection::new(self)
50 }
51
52 /// Accesses a [`schema::View`] from this connection.
53 fn view<V: schema::SerializedView>(&'_ self) -> View<'_, Self, V, V::Key> {
54 View::new(self)
55 }
56
57 /// Lists [executed transactions](transaction::Executed) from this
58 /// [`Schema`](schema::Schema). By default, a maximum of 1000 entries will
59 /// be returned, but that limit can be overridden by setting `result_limit`.
60 /// A hard limit of 100,000 results will be returned. To begin listing after
61 /// another known `transaction_id`, pass `transaction_id + 1` into
62 /// `starting_id`.
63 fn list_executed_transactions(
64 &self,
65 starting_id: Option<u64>,
66 result_limit: Option<u32>,
67 ) -> Result<Vec<transaction::Executed>, Error>;
68
69 /// Fetches the last transaction id that has been committed, if any.
70 fn last_transaction_id(&self) -> Result<Option<u64>, Error>;
71
72 /// Compacts the entire database to reclaim unused disk space.
73 ///
74 /// This process is done by writing data to a new file and swapping the file
75 /// once the process completes. This ensures that if a hardware failure,
76 /// power outage, or crash occurs that the original collection data is left
77 /// untouched.
78 ///
79 /// ## Errors
80 ///
81 /// * [`Error::Other`]: an error occurred while compacting the database.
82 fn compact(&self) -> Result<(), crate::Error>;
83
84 /// Compacts the collection to reclaim unused disk space.
85 ///
86 /// This process is done by writing data to a new file and swapping the file
87 /// once the process completes. This ensures that if a hardware failure,
88 /// power outage, or crash occurs that the original collection data is left
89 /// untouched.
90 ///
91 /// ## Errors
92 ///
93 /// * [`Error::CollectionNotFound`]: database `name` does not exist.
94 /// * [`Error::Other`]: an error occurred while compacting the database.
95 fn compact_collection<C: schema::Collection>(&self) -> Result<(), crate::Error> {
96 self.compact_collection_by_name(C::collection_name())
97 }
98
99 /// Compacts the key value store to reclaim unused disk space.
100 ///
101 /// This process is done by writing data to a new file and swapping the file
102 /// once the process completes. This ensures that if a hardware failure,
103 /// power outage, or crash occurs that the original collection data is left
104 /// untouched.
105 ///
106 /// ## Errors
107 ///
108 /// * [`Error::Other`]: an error occurred while compacting the database.
109 fn compact_key_value_store(&self) -> Result<(), crate::Error>;
110}
111
112/// Interacts with a collection over a `Connection`.
113///
114/// These examples in this type use this basic collection definition:
115///
116/// ```rust
117/// use bonsaidb_core::schema::Collection;
118/// use bonsaidb_core::Error;
119/// use serde::{Deserialize, Serialize};
120///
121/// #[derive(Debug, Serialize, Deserialize, Default, Collection)]
122/// #[collection(name = "MyCollection")]
123/// # #[collection(core = bonsaidb_core)]
124/// pub struct MyCollection {
125/// pub rank: u32,
126/// pub score: f32,
127/// }
128/// ```
129pub struct Collection<'a, Cn, Cl> {
130 connection: &'a Cn,
131 _phantom: PhantomData<Cl>, /* allows for extension traits to be written for collections of specific types */
132}
133
134impl<'a, Cn, Cl> Clone for Collection<'a, Cn, Cl> {
135 fn clone(&self) -> Self {
136 Self {
137 connection: self.connection,
138 _phantom: PhantomData,
139 }
140 }
141}
142
143impl<'a, Cn, Cl> Collection<'a, Cn, Cl>
144where
145 Cn: Connection,
146 Cl: schema::Collection,
147{
148 /// Creates a new instance using `connection`.
149 fn new(connection: &'a Cn) -> Self {
150 Self {
151 connection,
152 _phantom: PhantomData,
153 }
154 }
155
156 /// Adds a new `Document<Cl>` with the contents `item`.
157 ///
158 /// ## Automatic ID Assignment
159 ///
160 /// This function calls [`SerializedCollection::natural_id()`] to try to
161 /// retrieve a primary key value from `item`. If an id is returned, the item
162 /// is inserted with that id. If an id is not returned, an id will be
163 /// automatically assigned, if possible, by the storage backend, which uses the [`Key`]
164 /// trait to assign ids.
165 ///
166 /// ```rust
167 /// # bonsaidb_core::__doctest_prelude!();
168 /// # use bonsaidb_core::connection::Connection;
169 /// # fn test_fn<C: Connection>(db: &C) -> Result<(), Error> {
170 /// let inserted_header = db
171 /// .collection::<MyCollection>()
172 /// .push(&MyCollection::default())?;
173 /// println!(
174 /// "Inserted id {} with revision {}",
175 /// inserted_header.id, inserted_header.revision
176 /// );
177 /// # Ok(())
178 /// # }
179 /// ```
180 pub fn push(
181 &self,
182 item: &<Cl as SerializedCollection>::Contents,
183 ) -> Result<CollectionHeader<Cl::PrimaryKey>, crate::Error>
184 where
185 Cl: schema::SerializedCollection,
186 {
187 let contents = Cl::serialize(item)?;
188 if let Some(natural_id) = Cl::natural_id(item) {
189 self.insert_bytes(&natural_id, contents)
190 } else {
191 self.push_bytes(contents)
192 }
193 }
194
195 /// Adds a new `Document<Cl>` with the `contents`.
196 ///
197 /// ## Automatic ID Assignment
198 ///
199 /// An id will be automatically assigned, if possible, by the storage backend, which uses
200 /// the [`Key`] trait to assign ids.
201 ///
202 /// ```rust
203 /// # bonsaidb_core::__doctest_prelude!();
204 /// # use bonsaidb_core::connection::Connection;
205 /// # fn test_fn<C: Connection>(db: &C) -> Result<(), Error> {
206 /// let inserted_header = db.collection::<MyCollection>().push_bytes(vec![])?;
207 /// println!(
208 /// "Inserted id {} with revision {}",
209 /// inserted_header.id, inserted_header.revision
210 /// );
211 /// # Ok(())
212 /// # }
213 /// ```
214 pub fn push_bytes<B: Into<Bytes> + Send>(
215 &self,
216 contents: B,
217 ) -> Result<CollectionHeader<Cl::PrimaryKey>, crate::Error> {
218 self.connection
219 .insert::<Cl, _, B>(Option::<&Cl::PrimaryKey>::None, contents)
220 }
221
222 /// Adds a new `Document<Cl>` with the given `id` and contents `item`.
223 ///
224 /// ```rust
225 /// # bonsaidb_core::__doctest_prelude!();
226 /// # use bonsaidb_core::connection::Connection;
227 /// # fn test_fn<C: Connection>(db: &C) -> Result<(), Error> {
228 /// let inserted_header = db
229 /// .collection::<MyCollection>()
230 /// .insert(&42, &MyCollection::default())?;
231 /// println!(
232 /// "Inserted id {} with revision {}",
233 /// inserted_header.id, inserted_header.revision
234 /// );
235 /// # Ok(())
236 /// # }
237 /// ```
238 pub fn insert<PrimaryKey>(
239 &self,
240 id: &PrimaryKey,
241 item: &<Cl as SerializedCollection>::Contents,
242 ) -> Result<CollectionHeader<Cl::PrimaryKey>, crate::Error>
243 where
244 Cl: schema::SerializedCollection,
245 PrimaryKey: KeyEncoding<Cl::PrimaryKey> + ?Sized,
246 {
247 let contents = Cl::serialize(item)?;
248 self.connection.insert::<Cl, _, _>(Some(id), contents)
249 }
250
251 /// Adds a new `Document<Cl>` with the the given `id` and `contents`.
252 ///
253 /// ```rust
254 /// # bonsaidb_core::__doctest_prelude!();
255 /// # use bonsaidb_core::connection::Connection;
256 /// # fn test_fn<C: Connection>(db: &C) -> Result<(), Error> {
257 /// let inserted_header = db.collection::<MyCollection>().insert_bytes(&42, vec![])?;
258 /// println!(
259 /// "Inserted id {} with revision {}",
260 /// inserted_header.id, inserted_header.revision
261 /// );
262 /// # Ok(())
263 /// # }
264 /// ```
265 pub fn insert_bytes<PrimaryKey, B: Into<Bytes> + Send>(
266 &self,
267 id: &PrimaryKey,
268 contents: B,
269 ) -> Result<CollectionHeader<Cl::PrimaryKey>, crate::Error>
270 where
271 PrimaryKey: KeyEncoding<Cl::PrimaryKey> + ?Sized,
272 {
273 self.connection.insert::<Cl, _, B>(Some(id), contents)
274 }
275
276 /// Updates an existing document. Upon success, `doc.revision` will be
277 /// updated with the new revision.
278 ///
279 /// ```rust
280 /// # bonsaidb_core::__doctest_prelude!();
281 /// # use bonsaidb_core::connection::Connection;
282 /// # fn test_fn<C: Connection>(db: &C) -> Result<(), Error> {
283 /// if let Some(mut document) = db.collection::<MyCollection>().get(&42)? {
284 /// // modify the document
285 /// db.collection::<MyCollection>().update(&mut document);
286 /// println!("Updated revision: {:?}", document.header.revision);
287 /// }
288 /// # Ok(())
289 /// # }
290 /// ```
291 pub fn update<D: Document<Cl> + Send + Sync>(&self, doc: &mut D) -> Result<(), Error> {
292 self.connection.update::<Cl, D>(doc)
293 }
294
295 /// Overwrites an existing document, or inserts a new document. Upon success,
296 /// `doc.revision` will be updated with the new revision information.
297 ///
298 /// ```rust
299 /// # bonsaidb_core::__doctest_prelude!();
300 /// # use bonsaidb_core::connection::Connection;
301 /// # fn test_fn<C: Connection>(db: &C) -> Result<(), Error> {
302 /// if let Some(mut document) = db.collection::<MyCollection>().get(&42)? {
303 /// // modify the document
304 /// db.collection::<MyCollection>().overwrite(&mut document);
305 /// println!("Updated revision: {:?}", document.header.revision);
306 /// }
307 /// # Ok(())
308 /// # }
309 /// ```
310 pub fn overwrite<D: Document<Cl> + Send + Sync>(&self, doc: &mut D) -> Result<(), Error> {
311 let contents = doc.bytes()?;
312 doc.set_collection_header(self.connection.overwrite::<Cl, _>(doc.id(), contents)?)
313 }
314
315 /// Retrieves a `Document<Cl>` with `id` from the connection.
316 ///
317 /// ```rust
318 /// # bonsaidb_core::__doctest_prelude!();
319 /// # use bonsaidb_core::connection::Connection;
320 /// # fn test_fn<C: Connection>(db: &C) -> Result<(), Error> {
321 /// if let Some(doc) = db.collection::<MyCollection>().get(&42)? {
322 /// println!(
323 /// "Retrieved bytes {:?} with revision {}",
324 /// doc.contents, doc.header.revision
325 /// );
326 /// let deserialized = MyCollection::document_contents(&doc)?;
327 /// println!("Deserialized contents: {:?}", deserialized);
328 /// }
329 /// # Ok(())
330 /// # }
331 /// ```
332 pub fn get<PrimaryKey>(&self, id: &PrimaryKey) -> Result<Option<OwnedDocument>, Error>
333 where
334 PrimaryKey: KeyEncoding<Cl::PrimaryKey> + ?Sized,
335 {
336 self.connection.get::<Cl, _>(id)
337 }
338
339 /// Retrieves all documents matching `ids`. Documents that are not found
340 /// are not returned, but no error will be generated.
341 ///
342 /// ```rust
343 /// # bonsaidb_core::__doctest_prelude!();
344 /// # use bonsaidb_core::connection::Connection;
345 /// # fn test_fn<C: Connection>(db: &C) -> Result<(), Error> {
346 /// for doc in db.collection::<MyCollection>().get_multiple(&[42, 43])? {
347 /// println!("Retrieved #{} with bytes {:?}", doc.header.id, doc.contents);
348 /// let deserialized = MyCollection::document_contents(&doc)?;
349 /// println!("Deserialized contents: {:?}", deserialized);
350 /// }
351 /// # Ok(())
352 /// # }
353 /// ```
354 pub fn get_multiple<'id, DocumentIds, PrimaryKey, I>(
355 &self,
356 ids: DocumentIds,
357 ) -> Result<Vec<OwnedDocument>, Error>
358 where
359 DocumentIds: IntoIterator<Item = &'id PrimaryKey, IntoIter = I> + Send + Sync,
360 I: Iterator<Item = &'id PrimaryKey> + Send + Sync,
361 PrimaryKey: KeyEncoding<Cl::PrimaryKey> + 'id + ?Sized,
362 {
363 self.connection.get_multiple::<Cl, _, _, _>(ids)
364 }
365
366 /// Retrieves all documents matching the range of `ids`.
367 ///
368 /// ```rust
369 /// # bonsaidb_core::__doctest_prelude!();
370 /// # use bonsaidb_core::connection::Connection;
371 /// # fn test_fn<C: Connection>(db: &C) -> Result<(), Error> {
372 /// for doc in db
373 /// .collection::<MyCollection>()
374 /// .list(&42..)
375 /// .descending()
376 /// .limit(20)
377 /// .query()?
378 /// {
379 /// println!("Retrieved #{} with bytes {:?}", doc.header.id, doc.contents);
380 /// let deserialized = MyCollection::document_contents(&doc)?;
381 /// println!("Deserialized contents: {:?}", deserialized);
382 /// }
383 /// # Ok(())
384 /// # }
385 /// ```
386 pub fn list<PrimaryKey, R>(&'a self, ids: R) -> List<'a, Cn, Cl, PrimaryKey>
387 where
388 R: Into<Range<&'a PrimaryKey>>,
389 PrimaryKey: KeyEncoding<Cl::PrimaryKey> + PartialEq + 'a + ?Sized,
390 Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
391 {
392 List::new(MaybeOwned::Borrowed(self), RangeRef::borrowed(ids.into()))
393 }
394
395 /// Retrieves all documents with ids that start with `prefix`.
396 ///
397 /// ```rust
398 /// use bonsaidb_core::connection::Connection;
399 /// use bonsaidb_core::document::OwnedDocument;
400 /// use bonsaidb_core::schema::{Collection, Schematic, SerializedCollection};
401 /// use bonsaidb_core::Error;
402 /// use serde::{Deserialize, Serialize};
403 ///
404 /// #[derive(Debug, Serialize, Deserialize, Default, Collection)]
405 /// #[collection(name = "MyCollection", primary_key = String)]
406 /// # #[collection(core = bonsaidb_core)]
407 /// pub struct MyCollection;
408 ///
409 /// fn starts_with_a<C: Connection>(db: &C) -> Result<Vec<OwnedDocument>, Error> {
410 /// db.collection::<MyCollection>()
411 /// .list_with_prefix("a")
412 /// .query()
413 /// }
414 /// ```
415 pub fn list_with_prefix<PrimaryKey>(
416 &'a self,
417 prefix: &'a PrimaryKey,
418 ) -> List<'a, Cn, Cl, PrimaryKey>
419 where
420 PrimaryKey:
421 IntoPrefixRange<'a, Cl::PrimaryKey> + KeyEncoding<Cl::PrimaryKey> + PartialEq + ?Sized,
422 Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
423 {
424 List::new(MaybeOwned::Borrowed(self), prefix.to_prefix_range())
425 }
426
427 /// Retrieves all documents.
428 ///
429 /// ```rust
430 /// # bonsaidb_core::__doctest_prelude!();
431 /// # use bonsaidb_core::connection::Connection;
432 /// # fn test_fn<C: Connection>(db: &C) -> Result<(), Error> {
433 /// for doc in db.collection::<MyCollection>().all().query()? {
434 /// println!("Retrieved #{} with bytes {:?}", doc.header.id, doc.contents);
435 /// let deserialized = MyCollection::document_contents(&doc)?;
436 /// println!("Deserialized contents: {:?}", deserialized);
437 /// }
438 /// # Ok(())
439 /// # }
440 /// ```
441 pub fn all(&'a self) -> List<'a, Cn, Cl, Cl::PrimaryKey> {
442 List::new(
443 MaybeOwned::Borrowed(self),
444 RangeRef::borrowed(Range::from(..)),
445 )
446 }
447
448 /// Removes a `Document` from the database.
449 ///
450 /// ```rust
451 /// # bonsaidb_core::__doctest_prelude!();
452 /// # use bonsaidb_core::connection::Connection;
453 /// # fn test_fn<C: Connection>(db: &C) -> Result<(), Error> {
454 /// if let Some(doc) = db.collection::<MyCollection>().get(&42)? {
455 /// db.collection::<MyCollection>().delete(&doc)?;
456 /// }
457 /// # Ok(())
458 /// # }
459 /// ```
460 pub fn delete<H: HasHeader + Send + Sync>(&self, doc: &H) -> Result<(), Error> {
461 self.connection.delete::<Cl, H>(doc)
462 }
463}
464
465/// Retrieves a list of documents from a collection. This structure also offers
466/// functions to customize the options for the operation.
467#[must_use]
468pub struct List<'a, Cn, Cl, PrimaryKey>
469where
470 Cl: schema::Collection,
471 PrimaryKey: PartialEq + ?Sized,
472 Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
473{
474 collection: MaybeOwned<'a, Collection<'a, Cn, Cl>>,
475 range: RangeRef<'a, Cl::PrimaryKey, PrimaryKey>,
476 sort: Sort,
477 limit: Option<u32>,
478}
479
480impl<'a, Cn, Cl, PrimaryKey> List<'a, Cn, Cl, PrimaryKey>
481where
482 Cl: schema::Collection,
483 Cn: Connection,
484 PrimaryKey: KeyEncoding<Cl::PrimaryKey> + PartialEq + 'a + ?Sized,
485 Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
486{
487 pub(crate) const fn new(
488 collection: MaybeOwned<'a, Collection<'a, Cn, Cl>>,
489 range: RangeRef<'a, Cl::PrimaryKey, PrimaryKey>,
490 ) -> Self {
491 Self {
492 collection,
493 range,
494 sort: Sort::Ascending,
495 limit: None,
496 }
497 }
498
499 /// Lists documents by id in ascending order.
500 pub const fn ascending(mut self) -> Self {
501 self.sort = Sort::Ascending;
502 self
503 }
504
505 /// Lists documents by id in descending order.
506 pub const fn descending(mut self) -> Self {
507 self.sort = Sort::Descending;
508 self
509 }
510
511 /// Sets the maximum number of results to return.
512 pub const fn limit(mut self, maximum_results: u32) -> Self {
513 self.limit = Some(maximum_results);
514 self
515 }
516
517 /// Returns the number of documents contained within the range.
518 ///
519 /// Order and limit are ignored if they were set.
520 ///
521 /// ```rust
522 /// # bonsaidb_core::__doctest_prelude!();
523 /// # use bonsaidb_core::connection::Connection;
524 /// # fn test_fn<C: Connection>(db: &C) -> Result<(), Error> {
525 /// println!(
526 /// "Number of documents with id 42 or larger: {}",
527 /// db.collection::<MyCollection>().list(&42..).count()?
528 /// );
529 /// println!(
530 /// "Number of documents in MyCollection: {}",
531 /// db.collection::<MyCollection>().all().count()?
532 /// );
533 /// # Ok(())
534 /// # }
535 /// ```
536 pub fn count(self) -> Result<u64, Error> {
537 let Self {
538 collection, range, ..
539 } = self;
540 collection.connection.count::<Cl, _, _>(range)
541 }
542
543 /// Returns the list of headers for documents contained within the range.
544 ///
545 /// ```rust
546 /// # bonsaidb_core::__doctest_prelude!();
547 /// # use bonsaidb_core::connection::Connection;
548 /// # fn test_fn<C: Connection>(db: &C) -> Result<(), Error> {
549 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
550 /// println!(
551 /// "Headers with id 42 or larger: {:?}",
552 /// db.collection::<MyCollection>().list(&42..).headers()?
553 /// );
554 /// println!(
555 /// "Headers in MyCollection: {:?}",
556 /// db.collection::<MyCollection>().all().headers()?
557 /// );
558 /// # Ok(())
559 /// # })
560 /// # }
561 /// ```
562 pub fn headers(self) -> Result<Vec<Header>, Error> {
563 let Self {
564 collection,
565 range,
566 sort,
567 limit,
568 ..
569 } = self;
570 collection
571 .connection
572 .list_headers::<Cl, _, PrimaryKey>(range, sort, limit)
573 }
574
575 /// Retrieves the matching documents.
576 ///
577 /// ```rust
578 /// # bonsaidb_core::__doctest_prelude!();
579 /// # use bonsaidb_core::connection::Connection;
580 /// # fn test_fn<C: Connection>(db: &C) -> Result<(), Error> {
581 /// for doc in db.collection::<MyCollection>().all().query()? {
582 /// println!("Retrieved #{} with bytes {:?}", doc.header.id, doc.contents);
583 /// let deserialized = MyCollection::document_contents(&doc)?;
584 /// println!("Deserialized contents: {:?}", deserialized);
585 /// }
586 /// # Ok(())
587 /// # }
588 /// ```
589 pub fn query(self) -> Result<Vec<OwnedDocument>, Error> {
590 let Self {
591 collection,
592 range,
593 sort,
594 limit,
595 } = self;
596 collection.connection.list::<Cl, _, _>(range, sort, limit)
597 }
598}
599
600/// Parameters to query a [`schema::View`].
601///
602/// The examples for this type use this view definition:
603///
604/// ```rust
605/// # mod collection {
606/// # bonsaidb_core::__doctest_prelude!();
607/// # }
608/// # use collection::MyCollection;
609/// use bonsaidb_core::define_basic_unique_mapped_view;
610/// use bonsaidb_core::document::{CollectionDocument, Emit};
611/// use bonsaidb_core::schema::{
612/// CollectionMapReduce, DefaultViewSerialization, Name, ReduceResult, View, ViewMapResult,
613/// ViewMappedValue, ViewSchema,
614/// };
615///
616/// #[derive(Debug, Clone, View, ViewSchema)]
617/// #[view(collection = MyCollection, key = u32, value = f32, name = "scores-by-rank")]
618/// # #[view(core = bonsaidb_core)]
619/// # #[view_schema(core = bonsaidb_core)]
620/// pub struct ScoresByRank;
621///
622/// impl CollectionMapReduce for ScoresByRank {
623/// fn map<'doc>(
624/// &self,
625/// document: CollectionDocument<<Self::View as View>::Collection>,
626/// ) -> ViewMapResult<'doc, Self::View> {
627/// document
628/// .header
629/// .emit_key_and_value(document.contents.rank, document.contents.score)
630/// }
631///
632/// fn reduce(
633/// &self,
634/// mappings: &[ViewMappedValue<'_, Self::View>],
635/// rereduce: bool,
636/// ) -> ReduceResult<Self::View> {
637/// if mappings.is_empty() {
638/// Ok(0.)
639/// } else {
640/// Ok(mappings.iter().map(|map| map.value).sum::<f32>() / mappings.len() as f32)
641/// }
642/// }
643/// }
644/// ```
645#[must_use]
646pub struct View<'a, Cn, V: schema::SerializedView, Key>
647where
648 V::Key: Borrow<Key> + PartialEq<Key>,
649 Key: PartialEq + ?Sized,
650{
651 connection: &'a Cn,
652
653 /// Key filtering criteria.
654 pub key: Option<QueryKey<'a, V::Key, Key>>,
655
656 /// The view's data access policy. The default value is [`AccessPolicy::UpdateBefore`].
657 pub access_policy: AccessPolicy,
658
659 /// The sort order of the query.
660 pub sort: Sort,
661
662 /// The maximum number of results to return.
663 pub limit: Option<u32>,
664
665 _view: PhantomData<V>,
666}
667
668impl<'a, Cn, V, Key> View<'a, Cn, V, Key>
669where
670 V::Key: Borrow<Key> + PartialEq<Key>,
671 V: schema::SerializedView,
672 Cn: Connection,
673 Key: KeyEncoding<V::Key> + PartialEq + ?Sized,
674{
675 const fn new(connection: &'a Cn) -> Self {
676 Self {
677 connection,
678 key: None,
679 access_policy: AccessPolicy::UpdateBefore,
680 sort: Sort::Ascending,
681 limit: None,
682 _view: PhantomData,
683 }
684 }
685
686 /// Filters for entries in the view with `key`.
687 ///
688 /// ```rust
689 /// # bonsaidb_core::__doctest_prelude!();
690 /// # use bonsaidb_core::connection::Connection;
691 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
692 /// // score is an f32 in this example
693 /// for mapping in ScoresByRank::entries(&db).with_key(&42).query()? {
694 /// assert_eq!(mapping.key, 42);
695 /// println!("Rank {} has a score of {:3}", mapping.key, mapping.value);
696 /// }
697 /// # Ok(())
698 /// # }
699 /// ```
700 #[allow(clippy::missing_const_for_fn)] // false positive, destructors
701 pub fn with_key<K>(self, key: &'a K) -> View<'a, Cn, V, K>
702 where
703 K: KeyEncoding<V::Key> + PartialEq + ?Sized,
704 V::Key: Borrow<K> + PartialEq<K>,
705 {
706 View {
707 connection: self.connection,
708 key: Some(QueryKey::Matches(MaybeOwned::Borrowed(key))),
709 access_policy: self.access_policy,
710 sort: self.sort,
711 limit: self.limit,
712 _view: PhantomData,
713 }
714 }
715
716 /// Filters for entries in the view with `keys`.
717 ///
718 /// ```rust
719 /// # bonsaidb_core::__doctest_prelude!();
720 /// # use bonsaidb_core::connection::Connection;
721 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
722 /// // score is an f32 in this example
723 /// for mapping in ScoresByRank::entries(&db).with_keys(&[42, 43]).query()? {
724 /// println!("Rank {} has a score of {:3}", mapping.key, mapping.value);
725 /// }
726 /// # Ok(())
727 /// # }
728 /// ```
729 pub fn with_keys<K, IntoIter: IntoIterator<Item = &'a K>>(
730 self,
731 keys: IntoIter,
732 ) -> View<'a, Cn, V, K>
733 where
734 V::Key: Borrow<K> + PartialEq<K>,
735 K: PartialEq + ?Sized,
736 {
737 View {
738 connection: self.connection,
739 key: Some(QueryKey::Multiple(
740 keys.into_iter().map(MaybeOwned::Borrowed).collect(),
741 )),
742 access_policy: self.access_policy,
743 sort: self.sort,
744 limit: self.limit,
745 _view: PhantomData,
746 }
747 }
748
749 /// Filters for entries in the view with the range `keys`.
750 ///
751 /// ```rust
752 /// # bonsaidb_core::__doctest_prelude!();
753 /// # use bonsaidb_core::connection::Connection;
754 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
755 /// // score is an f32 in this example
756 /// for mapping in ScoresByRank::entries(&db).with_key_range(42..).query()? {
757 /// assert!(mapping.key >= 42);
758 /// println!("Rank {} has a score of {:3}", mapping.key, mapping.value);
759 /// }
760 /// # Ok(())
761 /// # }
762 /// ```
763 pub fn with_key_range<K, R>(self, range: R) -> View<'a, Cn, V, K>
764 where
765 R: Into<RangeRef<'a, V::Key, K>>,
766 K: PartialEq,
767 V::Key: Borrow<K> + PartialEq<K>,
768 {
769 View {
770 connection: self.connection,
771 key: Some(QueryKey::Range(range.into())),
772 access_policy: self.access_policy,
773 sort: self.sort,
774 limit: self.limit,
775 _view: PhantomData,
776 }
777 }
778
779 /// Filters for entries in the view with keys that begin with `prefix`.
780 ///
781 /// ```rust
782 /// # bonsaidb_core::__doctest_prelude!();
783 /// # use bonsaidb_core::connection::Connection;
784 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
785 /// #[derive(View, Debug, Clone)]
786 /// #[view(name = "by-name", key = String, collection = MyCollection)]
787 /// # #[view(core = bonsaidb_core)]
788 /// struct ByName;
789 ///
790 /// // score is an f32 in this example
791 /// for mapping in ByName::entries(&db).with_key_prefix("a").query()? {
792 /// assert!(mapping.key.starts_with("a"));
793 /// println!("{} in document {:?}", mapping.key, mapping.source);
794 /// }
795 /// # Ok(())
796 /// # }
797 /// ```
798 pub fn with_key_prefix<K>(self, prefix: &'a K) -> View<'a, Cn, V, K>
799 where
800 K: KeyEncoding<V::Key> + IntoPrefixRange<'a, V::Key> + PartialEq + ?Sized,
801 V::Key: Borrow<K> + PartialEq<K>,
802 {
803 View {
804 connection: self.connection,
805 key: Some(QueryKey::Range(prefix.to_prefix_range())),
806 access_policy: self.access_policy,
807 sort: self.sort,
808 limit: self.limit,
809 _view: PhantomData,
810 }
811 }
812
813 /// Sets the access policy for queries.
814 ///
815 /// ```rust
816 /// # bonsaidb_core::__doctest_prelude!();
817 /// # use bonsaidb_core::connection::Connection;
818 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
819 /// // score is an f32 in this example
820 /// for mapping in ScoresByRank::entries(&db)
821 /// .with_access_policy(AccessPolicy::UpdateAfter)
822 /// .query()?
823 /// {
824 /// println!("Rank {} has a score of {:3}", mapping.key, mapping.value);
825 /// }
826 /// # Ok(())
827 /// # }
828 /// ```
829 pub const fn with_access_policy(mut self, policy: AccessPolicy) -> Self {
830 self.access_policy = policy;
831 self
832 }
833
834 /// Returns the matching mappings in ascending key order. This is the
835 /// default sorting behavior.
836 ///
837 /// When more than one mapping exists for a single key, all matching
838 /// mappings are returned as a unique entry. The resulting mappings are
839 /// sorted only by the key, and as such, the order of mappings with the same
840 /// key is undefined.
841 ///
842 /// ```rust
843 /// # bonsaidb_core::__doctest_prelude!();
844 /// # use bonsaidb_core::connection::Connection;
845 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
846 /// // score is an f32 in this example
847 /// for mapping in ScoresByRank::entries(&db).ascending().query()? {
848 /// println!("Rank {} has a score of {:3}", mapping.key, mapping.value);
849 /// }
850 /// # Ok(())
851 /// # }
852 /// ```
853 pub const fn ascending(mut self) -> Self {
854 self.sort = Sort::Ascending;
855 self
856 }
857
858 /// Returns the matching mappings in descending key order.
859 ///
860 /// When more than one mapping exists for a single key, all matching
861 /// mappings are returned as a unique entry. The resulting mappings are
862 /// sorted only by the key, and as such, the order of mappings with the same
863 /// key is undefined.
864 ///
865 /// ```rust
866 /// # bonsaidb_core::__doctest_prelude!();
867 /// # use bonsaidb_core::connection::Connection;
868 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
869 /// // score is an f32 in this example
870 /// for mapping in ScoresByRank::entries(&db).descending().query()? {
871 /// println!("Rank {} has a score of {:3}", mapping.key, mapping.value);
872 /// }
873 /// # Ok(())
874 /// # }
875 /// ```
876 pub const fn descending(mut self) -> Self {
877 self.sort = Sort::Descending;
878 self
879 }
880
881 /// Sets the maximum number of results to return.
882 ///
883 /// ```rust
884 /// # bonsaidb_core::__doctest_prelude!();
885 /// # use bonsaidb_core::connection::Connection;
886 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
887 /// // score is an f32 in this example
888 /// let mappings = ScoresByRank::entries(&db).limit(10).query()?;
889 /// assert!(mappings.len() <= 10);
890 /// # Ok(())
891 /// # }
892 /// ```
893 pub const fn limit(mut self, maximum_results: u32) -> Self {
894 self.limit = Some(maximum_results);
895 self
896 }
897
898 /// Executes the query and retrieves the results.
899 ///
900 /// ```rust
901 /// # bonsaidb_core::__doctest_prelude!();
902 /// # use bonsaidb_core::connection::Connection;
903 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
904 /// // score is an f32 in this example
905 /// for mapping in ScoresByRank::entries(&db).query()? {
906 /// println!("Rank {} has a score of {:3}", mapping.key, mapping.value);
907 /// }
908 /// # Ok(())
909 /// # }
910 /// ```
911 pub fn query(self) -> Result<ViewMappingsCurrent<V>, Error> {
912 self.connection
913 .query::<V, Key>(self.key, self.sort, self.limit, self.access_policy)
914 }
915
916 /// Executes the query and retrieves the results with the associated [`Document`s](crate::document::OwnedDocument).
917 ///
918 /// ```rust
919 /// # bonsaidb_core::__doctest_prelude!();
920 /// # use bonsaidb_core::connection::Connection;
921 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
922 /// for mapping in &ScoresByRank::entries(&db)
923 /// .with_key_range(42..=44)
924 /// .query_with_docs()?
925 /// {
926 /// println!(
927 /// "Mapping from #{} with rank: {} and score: {}. Document bytes: {:?}",
928 /// mapping.document.header.id, mapping.key, mapping.value, mapping.document.contents
929 /// );
930 /// }
931 /// # Ok(())
932 /// # }
933 /// ```
934 pub fn query_with_docs(self) -> Result<MappedDocuments<OwnedDocument, V>, Error> {
935 self.connection.query_with_docs::<V, Key>(
936 self.key,
937 self.sort,
938 self.limit,
939 self.access_policy,
940 )
941 }
942
943 /// Executes the query and retrieves the results with the associated [`CollectionDocument`s](crate::document::CollectionDocument).
944 ///
945 /// ```rust
946 /// # bonsaidb_core::__doctest_prelude!();
947 /// # use bonsaidb_core::connection::Connection;
948 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
949 /// for mapping in &ScoresByRank::entries(&db)
950 /// .with_key_range(42..=44)
951 /// .query_with_collection_docs()?
952 /// {
953 /// println!(
954 /// "Mapping from #{} with rank: {} and score: {}. Deserialized Contents: {:?}",
955 /// mapping.document.header.id, mapping.key, mapping.value, mapping.document.contents
956 /// );
957 /// }
958 /// # Ok(())
959 /// # }
960 /// ```
961 pub fn query_with_collection_docs(
962 self,
963 ) -> Result<MappedDocuments<CollectionDocument<V::Collection>, V>, Error>
964 where
965 V::Collection: SerializedCollection,
966 <V::Collection as SerializedCollection>::Contents: std::fmt::Debug,
967 {
968 self.connection.query_with_collection_docs::<V, Key>(
969 self.key,
970 self.sort,
971 self.limit,
972 self.access_policy,
973 )
974 }
975
976 /// Executes a reduce over the results of the query
977 ///
978 /// ```rust
979 /// # bonsaidb_core::__doctest_prelude!();
980 /// # use bonsaidb_core::connection::Connection;
981 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
982 /// // score is an f32 in this example
983 /// let score = ScoresByRank::entries(&db).reduce()?;
984 /// println!("Average score: {:3}", score);
985 /// # Ok(())
986 /// # }
987 /// ```
988 pub fn reduce(self) -> Result<V::Value, Error> {
989 self.connection
990 .reduce::<V, Key>(self.key, self.access_policy)
991 }
992
993 /// Executes a reduce over the results of the query, grouping by key.
994 ///
995 /// ```rust
996 /// # bonsaidb_core::__doctest_prelude!();
997 /// # use bonsaidb_core::connection::Connection;
998 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
999 /// // score is an f32 in this example
1000 /// for mapping in ScoresByRank::entries(&db).reduce_grouped()? {
1001 /// println!(
1002 /// "Rank {} has an average score of {:3}",
1003 /// mapping.key, mapping.value
1004 /// );
1005 /// }
1006 /// # Ok(())
1007 /// # }
1008 /// ```
1009 pub fn reduce_grouped(self) -> Result<GroupedReductions<V>, Error> {
1010 self.connection
1011 .reduce_grouped::<V, Key>(self.key, self.access_policy)
1012 }
1013
1014 /// Deletes all of the associated documents that match this view query.
1015 ///
1016 /// ```rust
1017 /// # bonsaidb_core::__doctest_prelude!();
1018 /// # use bonsaidb_core::connection::Connection;
1019 /// # fn test_fn<C: Connection>(db: C) -> Result<(), Error> {
1020 /// ScoresByRank::entries(&db).delete_docs()?;
1021 /// # Ok(())
1022 /// # }
1023 /// ```
1024 pub fn delete_docs(self) -> Result<u64, Error> {
1025 self.connection
1026 .delete_docs::<V, Key>(self.key, self.access_policy)
1027 }
1028}
1029
1030/// This type is the result of `reduce_grouped()`. It is a list of all matching
1031/// keys and the reduced value of all mapped entries for that key.
1032pub type GroupedReductions<V> =
1033 Vec<MappedValue<<V as schema::View>::Key, <V as schema::View>::Value>>;
1034
1035/// A connection to a database's [`Schema`](schema::Schema), giving access to
1036/// [`Collection`s](crate::schema::Collection) and
1037/// [`Views`s](crate::schema::View). All functions on this trait are safe to use
1038/// in an asynchronous context.
1039#[async_trait]
1040pub trait AsyncConnection: AsyncLowLevelConnection + Sized + Send + Sync {
1041 /// The [`AsyncStorageConnection`] type that is paired with this type.
1042 type Storage: AsyncStorageConnection<Database = Self>;
1043
1044 /// Returns the [`StorageConnection`] implementor that this database belongs
1045 /// to.
1046 fn storage(&self) -> Self::Storage;
1047
1048 /// Accesses a collection for the connected [`Schema`](schema::Schema).
1049 fn collection<C: schema::Collection>(&self) -> AsyncCollection<'_, Self, C> {
1050 AsyncCollection::new(self)
1051 }
1052
1053 /// Accesses a [`schema::View`] from this connection.
1054 fn view<V: schema::SerializedView>(&'_ self) -> AsyncView<'_, Self, V, V::Key> {
1055 AsyncView::new(self)
1056 }
1057
1058 /// Lists [executed transactions](transaction::Executed) from this [`Schema`](schema::Schema). By default, a maximum of
1059 /// 1000 entries will be returned, but that limit can be overridden by
1060 /// setting `result_limit`. A hard limit of 100,000 results will be
1061 /// returned. To begin listing after another known `transaction_id`, pass
1062 /// `transaction_id + 1` into `starting_id`.
1063 async fn list_executed_transactions(
1064 &self,
1065 starting_id: Option<u64>,
1066 result_limit: Option<u32>,
1067 ) -> Result<Vec<transaction::Executed>, Error>;
1068
1069 /// Fetches the last transaction id that has been committed, if any.
1070 async fn last_transaction_id(&self) -> Result<Option<u64>, Error>;
1071
1072 /// Compacts the entire database to reclaim unused disk space.
1073 ///
1074 /// This process is done by writing data to a new file and swapping the file
1075 /// once the process completes. This ensures that if a hardware failure,
1076 /// power outage, or crash occurs that the original collection data is left
1077 /// untouched.
1078 ///
1079 /// ## Errors
1080 ///
1081 /// * [`Error::Other`]: an error occurred while compacting the database.
1082 async fn compact(&self) -> Result<(), crate::Error>;
1083
1084 /// Compacts the collection to reclaim unused disk space.
1085 ///
1086 /// This process is done by writing data to a new file and swapping the file
1087 /// once the process completes. This ensures that if a hardware failure,
1088 /// power outage, or crash occurs that the original collection data is left
1089 /// untouched.
1090 ///
1091 /// ## Errors
1092 ///
1093 /// * [`Error::CollectionNotFound`]: database `name` does not exist.
1094 /// * [`Error::Other`]: an error occurred while compacting the database.
1095 async fn compact_collection<C: schema::Collection>(&self) -> Result<(), crate::Error> {
1096 self.compact_collection_by_name(C::collection_name()).await
1097 }
1098
1099 /// Compacts the key value store to reclaim unused disk space.
1100 ///
1101 /// This process is done by writing data to a new file and swapping the file
1102 /// once the process completes. This ensures that if a hardware failure,
1103 /// power outage, or crash occurs that the original collection data is left
1104 /// untouched.
1105 ///
1106 /// ## Errors
1107 ///
1108 /// * [`Error::Other`]: an error occurred while compacting the database.
1109 async fn compact_key_value_store(&self) -> Result<(), crate::Error>;
1110}
1111
1112/// Interacts with a collection over a `Connection`.
1113///
1114/// These examples in this type use this basic collection definition:
1115///
1116/// ```rust
1117/// use bonsaidb_core::schema::Collection;
1118/// use bonsaidb_core::Error;
1119/// use serde::{Deserialize, Serialize};
1120///
1121/// #[derive(Debug, Serialize, Deserialize, Default, Collection)]
1122/// #[collection(name = "MyCollection")]
1123/// # #[collection(core = bonsaidb_core)]
1124/// pub struct MyCollection {
1125/// pub rank: u32,
1126/// pub score: f32,
1127/// }
1128/// ```
1129pub struct AsyncCollection<'a, Cn, Cl> {
1130 connection: &'a Cn,
1131 _phantom: PhantomData<Cl>, /* allows for extension traits to be written for collections of specific types */
1132}
1133
1134impl<'a, Cn, Cl> Clone for AsyncCollection<'a, Cn, Cl> {
1135 fn clone(&self) -> Self {
1136 Self {
1137 connection: self.connection,
1138 _phantom: PhantomData,
1139 }
1140 }
1141}
1142
1143impl<'a, Cn, Cl> AsyncCollection<'a, Cn, Cl>
1144where
1145 Cn: AsyncConnection,
1146 Cl: schema::Collection,
1147{
1148 /// Creates a new instance using `connection`.
1149 fn new(connection: &'a Cn) -> Self {
1150 Self {
1151 connection,
1152 _phantom: PhantomData,
1153 }
1154 }
1155
1156 /// Adds a new `Document<Cl>` with the contents `item`.
1157 ///
1158 /// ## Automatic ID Assignment
1159 ///
1160 /// This function calls [`SerializedCollection::natural_id()`] to try to
1161 /// retrieve a primary key value from `item`. If an id is returned, the item
1162 /// is inserted with that id. If an id is not returned, an id will be
1163 /// automatically assigned, if possible, by the storage backend, which uses the [`Key`]
1164 /// trait to assign ids.
1165 ///
1166 /// ```rust
1167 /// # bonsaidb_core::__doctest_prelude!();
1168 /// # use bonsaidb_core::connection::AsyncConnection;
1169 /// # fn test_fn<C: AsyncConnection>(db: &C) -> Result<(), Error> {
1170 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1171 /// let inserted_header = db
1172 /// .collection::<MyCollection>()
1173 /// .push(&MyCollection::default())
1174 /// .await?;
1175 /// println!(
1176 /// "Inserted id {} with revision {}",
1177 /// inserted_header.id, inserted_header.revision
1178 /// );
1179 /// # Ok(())
1180 /// # })
1181 /// # }
1182 /// ```
1183 pub async fn push(
1184 &self,
1185 item: &<Cl as SerializedCollection>::Contents,
1186 ) -> Result<CollectionHeader<Cl::PrimaryKey>, crate::Error>
1187 where
1188 Cl: schema::SerializedCollection,
1189 {
1190 let contents = Cl::serialize(item)?;
1191 if let Some(natural_id) = Cl::natural_id(item) {
1192 self.insert_bytes(&natural_id, contents).await
1193 } else {
1194 self.push_bytes(contents).await
1195 }
1196 }
1197
1198 /// Adds a new `Document<Cl>` with the `contents`.
1199 ///
1200 /// ## Automatic ID Assignment
1201 ///
1202 /// An id will be automatically assigned, if possible, by the storage backend, which uses
1203 /// the [`Key`] trait to assign ids.
1204 ///
1205 /// ```rust
1206 /// # bonsaidb_core::__doctest_prelude!();
1207 /// # use bonsaidb_core::connection::AsyncConnection;
1208 /// # fn test_fn<C: AsyncConnection>(db: &C) -> Result<(), Error> {
1209 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1210 /// let inserted_header = db.collection::<MyCollection>().push_bytes(vec![]).await?;
1211 /// println!(
1212 /// "Inserted id {} with revision {}",
1213 /// inserted_header.id, inserted_header.revision
1214 /// );
1215 /// # Ok(())
1216 /// # })
1217 /// # }
1218 /// ```
1219 pub async fn push_bytes<B: Into<Bytes> + Send>(
1220 &self,
1221 contents: B,
1222 ) -> Result<CollectionHeader<Cl::PrimaryKey>, crate::Error> {
1223 self.connection
1224 .insert::<Cl, _, B>(Option::<&Cl::PrimaryKey>::None, contents)
1225 .await
1226 }
1227
1228 /// Adds a new `Document<Cl>` with the given `id` and contents `item`.
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 inserted_header = db
1236 /// .collection::<MyCollection>()
1237 /// .insert(&42, &MyCollection::default())
1238 /// .await?;
1239 /// println!(
1240 /// "Inserted id {} with revision {}",
1241 /// inserted_header.id, inserted_header.revision
1242 /// );
1243 /// # Ok(())
1244 /// # })
1245 /// # }
1246 /// ```
1247 pub async fn insert<PrimaryKey>(
1248 &self,
1249 id: &PrimaryKey,
1250 item: &<Cl as SerializedCollection>::Contents,
1251 ) -> Result<CollectionHeader<Cl::PrimaryKey>, crate::Error>
1252 where
1253 Cl: schema::SerializedCollection,
1254 PrimaryKey: KeyEncoding<Cl::PrimaryKey> + ?Sized,
1255 {
1256 let contents = Cl::serialize(item)?;
1257 self.connection.insert::<Cl, _, _>(Some(id), contents).await
1258 }
1259
1260 /// Adds a new `Document<Cl>` with the the given `id` and `contents`.
1261 ///
1262 /// ```rust
1263 /// # bonsaidb_core::__doctest_prelude!();
1264 /// # use bonsaidb_core::connection::AsyncConnection;
1265 /// # fn test_fn<C: AsyncConnection>(db: &C) -> Result<(), Error> {
1266 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1267 /// let inserted_header = db
1268 /// .collection::<MyCollection>()
1269 /// .insert_bytes(&42, vec![])
1270 /// .await?;
1271 /// println!(
1272 /// "Inserted id {} with revision {}",
1273 /// inserted_header.id, inserted_header.revision
1274 /// );
1275 /// # Ok(())
1276 /// # })
1277 /// # }
1278 /// ```
1279 pub async fn insert_bytes<PrimaryKey, B: Into<Bytes> + Send>(
1280 &self,
1281 id: &PrimaryKey,
1282 contents: B,
1283 ) -> Result<CollectionHeader<Cl::PrimaryKey>, crate::Error>
1284 where
1285 PrimaryKey: KeyEncoding<Cl::PrimaryKey> + ?Sized,
1286 {
1287 self.connection.insert::<Cl, _, B>(Some(id), contents).await
1288 }
1289
1290 /// Updates an existing document. Upon success, `doc.revision` will be
1291 /// updated with the new revision.
1292 ///
1293 /// ```rust
1294 /// # bonsaidb_core::__doctest_prelude!();
1295 /// # use bonsaidb_core::connection::AsyncConnection;
1296 /// # fn test_fn<C: AsyncConnection>(db: &C) -> Result<(), Error> {
1297 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1298 /// if let Some(mut document) = db.collection::<MyCollection>().get(&42).await? {
1299 /// // modify the document
1300 /// db.collection::<MyCollection>().update(&mut document);
1301 /// println!("Updated revision: {:?}", document.header.revision);
1302 /// }
1303 /// # Ok(())
1304 /// # })
1305 /// # }
1306 /// ```
1307 pub async fn update<D: Document<Cl> + Send + Sync>(&self, doc: &mut D) -> Result<(), Error> {
1308 self.connection.update::<Cl, D>(doc).await
1309 }
1310
1311 /// Overwrites an existing document, or inserts a new document. Upon success,
1312 /// `doc.revision` will be updated with the new revision information.
1313 ///
1314 /// ```rust
1315 /// # bonsaidb_core::__doctest_prelude!();
1316 /// # use bonsaidb_core::connection::AsyncConnection;
1317 /// # fn test_fn<C: AsyncConnection>(db: &C) -> Result<(), Error> {
1318 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1319 /// if let Some(mut document) = db.collection::<MyCollection>().get(&42).await? {
1320 /// // modify the document
1321 /// db.collection::<MyCollection>().overwrite(&mut document);
1322 /// println!("Updated revision: {:?}", document.header.revision);
1323 /// }
1324 /// # Ok(())
1325 /// # })
1326 /// # }
1327 /// ```
1328 pub async fn overwrite<D: Document<Cl> + Send + Sync>(&self, doc: &mut D) -> Result<(), Error> {
1329 let contents = doc.bytes()?;
1330 doc.set_collection_header(
1331 self.connection
1332 .overwrite::<Cl, _>(doc.id(), contents)
1333 .await?,
1334 )
1335 }
1336
1337 /// Retrieves a `Document<Cl>` with `id` from the connection.
1338 ///
1339 /// ```rust
1340 /// # bonsaidb_core::__doctest_prelude!();
1341 /// # use bonsaidb_core::connection::AsyncConnection;
1342 /// # fn test_fn<C: AsyncConnection>(db: &C) -> Result<(), Error> {
1343 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1344 /// if let Some(doc) = db.collection::<MyCollection>().get(&42).await? {
1345 /// println!(
1346 /// "Retrieved bytes {:?} with revision {}",
1347 /// doc.contents, doc.header.revision
1348 /// );
1349 /// let deserialized = MyCollection::document_contents(&doc)?;
1350 /// println!("Deserialized contents: {:?}", deserialized);
1351 /// }
1352 /// # Ok(())
1353 /// # })
1354 /// # }
1355 /// ```
1356 pub async fn get<PrimaryKey>(&self, id: &PrimaryKey) -> Result<Option<OwnedDocument>, Error>
1357 where
1358 PrimaryKey: KeyEncoding<Cl::PrimaryKey> + ?Sized,
1359 {
1360 self.connection.get::<Cl, _>(id).await
1361 }
1362
1363 /// Retrieves all documents matching `ids`. Documents that are not found
1364 /// are not returned, but no error will be generated.
1365 ///
1366 /// ```rust
1367 /// # bonsaidb_core::__doctest_prelude!();
1368 /// # use bonsaidb_core::connection::AsyncConnection;
1369 /// # fn test_fn<C: AsyncConnection>(db: &C) -> Result<(), Error> {
1370 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1371 /// for doc in db
1372 /// .collection::<MyCollection>()
1373 /// .get_multiple(&[42, 43])
1374 /// .await?
1375 /// {
1376 /// println!("Retrieved #{} with bytes {:?}", doc.header.id, doc.contents);
1377 /// let deserialized = MyCollection::document_contents(&doc)?;
1378 /// println!("Deserialized contents: {:?}", deserialized);
1379 /// }
1380 /// # Ok(())
1381 /// # })
1382 /// # }
1383 /// ```
1384 pub async fn get_multiple<'id, DocumentIds, PrimaryKey, I>(
1385 &self,
1386 ids: DocumentIds,
1387 ) -> Result<Vec<OwnedDocument>, Error>
1388 where
1389 DocumentIds: IntoIterator<Item = &'id PrimaryKey, IntoIter = I> + Send + Sync,
1390 I: Iterator<Item = &'id PrimaryKey> + Send + Sync,
1391 PrimaryKey: KeyEncoding<Cl::PrimaryKey> + 'id + ?Sized,
1392 {
1393 self.connection.get_multiple::<Cl, _, _, _>(ids).await
1394 }
1395
1396 /// Retrieves all documents matching the range of `ids`.
1397 ///
1398 /// ```rust
1399 /// # bonsaidb_core::__doctest_prelude!();
1400 /// # use bonsaidb_core::connection::AsyncConnection;
1401 /// # fn test_fn<C: AsyncConnection>(db: &C) -> Result<(), Error> {
1402 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1403 /// for doc in db
1404 /// .collection::<MyCollection>()
1405 /// .list(42..)
1406 /// .descending()
1407 /// .limit(20)
1408 /// .await?
1409 /// {
1410 /// println!("Retrieved #{} with bytes {:?}", doc.header.id, doc.contents);
1411 /// let deserialized = MyCollection::document_contents(&doc)?;
1412 /// println!("Deserialized contents: {:?}", deserialized);
1413 /// }
1414 /// # Ok(())
1415 /// # })
1416 /// # }
1417 /// ```
1418 pub fn list<PrimaryKey, R>(&'a self, ids: R) -> AsyncList<'a, Cn, Cl, PrimaryKey>
1419 where
1420 R: Into<RangeRef<'a, Cl::PrimaryKey, PrimaryKey>>,
1421 PrimaryKey: KeyEncoding<Cl::PrimaryKey> + PartialEq + ?Sized,
1422 Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
1423 {
1424 AsyncList::new(MaybeOwned::Borrowed(self), ids.into())
1425 }
1426
1427 /// Retrieves all documents with ids that start with `prefix`.
1428 ///
1429 /// ```rust
1430 /// use bonsaidb_core::connection::AsyncConnection;
1431 /// use bonsaidb_core::document::OwnedDocument;
1432 /// use bonsaidb_core::schema::{Collection, Schematic, SerializedCollection};
1433 /// use bonsaidb_core::Error;
1434 /// use serde::{Deserialize, Serialize};
1435 ///
1436 /// #[derive(Debug, Serialize, Deserialize, Default, Collection)]
1437 /// #[collection(name = "MyCollection", primary_key = String)]
1438 /// # #[collection(core = bonsaidb_core)]
1439 /// pub struct MyCollection;
1440 ///
1441 /// async fn starts_with_a<C: AsyncConnection>(db: &C) -> Result<Vec<OwnedDocument>, Error> {
1442 /// db.collection::<MyCollection>().list_with_prefix("a").await
1443 /// }
1444 /// ```
1445 pub fn list_with_prefix<PrimaryKey>(
1446 &'a self,
1447 prefix: &'a PrimaryKey,
1448 ) -> AsyncList<'a, Cn, Cl, PrimaryKey>
1449 where
1450 PrimaryKey:
1451 IntoPrefixRange<'a, Cl::PrimaryKey> + KeyEncoding<Cl::PrimaryKey> + PartialEq + ?Sized,
1452 Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
1453 {
1454 AsyncList::new(MaybeOwned::Borrowed(self), prefix.to_prefix_range())
1455 }
1456
1457 /// Retrieves all documents.
1458 ///
1459 /// ```rust
1460 /// # bonsaidb_core::__doctest_prelude!();
1461 /// # use bonsaidb_core::connection::AsyncConnection;
1462 /// # fn test_fn<C: AsyncConnection>(db: &C) -> Result<(), Error> {
1463 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1464 /// for doc in db.collection::<MyCollection>().all().await? {
1465 /// println!("Retrieved #{} with bytes {:?}", doc.header.id, doc.contents);
1466 /// let deserialized = MyCollection::document_contents(&doc)?;
1467 /// println!("Deserialized contents: {:?}", deserialized);
1468 /// }
1469 /// # Ok(())
1470 /// # })
1471 /// # }
1472 /// ```
1473 pub fn all(&'a self) -> AsyncList<'a, Cn, Cl, Cl::PrimaryKey> {
1474 AsyncList::new(MaybeOwned::Borrowed(self), RangeRef::from(..))
1475 }
1476
1477 /// Removes a `Document` from the database.
1478 ///
1479 /// ```rust
1480 /// # bonsaidb_core::__doctest_prelude!();
1481 /// # use bonsaidb_core::connection::AsyncConnection;
1482 /// # fn test_fn<C: AsyncConnection>(db: &C) -> Result<(), Error> {
1483 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1484 /// if let Some(doc) = db.collection::<MyCollection>().get(&42).await? {
1485 /// db.collection::<MyCollection>().delete(&doc).await?;
1486 /// }
1487 /// # Ok(())
1488 /// # })
1489 /// # }
1490 /// ```
1491 pub async fn delete<H: HasHeader + Send + Sync>(&self, doc: &H) -> Result<(), Error> {
1492 self.connection.delete::<Cl, H>(doc).await
1493 }
1494}
1495
1496pub(crate) struct AsyncListBuilder<'a, Cn, Cl, PrimaryKey>
1497where
1498 Cl: schema::Collection,
1499 PrimaryKey: KeyEncoding<Cl::PrimaryKey> + PartialEq + ?Sized,
1500 Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
1501{
1502 collection: MaybeOwned<'a, AsyncCollection<'a, Cn, Cl>>,
1503 range: RangeRef<'a, Cl::PrimaryKey, PrimaryKey>,
1504 sort: Sort,
1505 limit: Option<u32>,
1506}
1507
1508/// A value that may be owned or not. Similar to [`std::borrow::Cow`] but does
1509/// not require `Clone`.
1510#[derive(Debug)]
1511pub enum MaybeOwned<'a, TOwned, TBorrowed: ?Sized = TOwned> {
1512 /// An owned value.
1513 Owned(TOwned),
1514 /// A borrowed value.
1515 Borrowed(&'a TBorrowed),
1516}
1517
1518impl<'a, TOwned, TBorrowed> Clone for MaybeOwned<'a, TOwned, TBorrowed>
1519where
1520 TOwned: Clone,
1521 TBorrowed: ?Sized,
1522{
1523 fn clone(&self) -> Self {
1524 match self {
1525 Self::Owned(value) => Self::Owned(value.clone()),
1526 Self::Borrowed(value) => Self::Borrowed(value),
1527 }
1528 }
1529}
1530
1531impl<'a, TOwned, TBorrowed> Deref for MaybeOwned<'a, TOwned, TBorrowed>
1532where
1533 TOwned: Borrow<TBorrowed>,
1534 TBorrowed: ?Sized,
1535{
1536 type Target = TBorrowed;
1537
1538 fn deref(&self) -> &TBorrowed {
1539 self.borrow()
1540 }
1541}
1542
1543impl<'a, TOwned, TBorrowed> Borrow<TBorrowed> for MaybeOwned<'a, TOwned, TBorrowed>
1544where
1545 TOwned: Borrow<TBorrowed>,
1546 TBorrowed: ?Sized,
1547{
1548 fn borrow(&self) -> &TBorrowed {
1549 match self {
1550 MaybeOwned::Owned(value) => value.borrow(),
1551 MaybeOwned::Borrowed(value) => value,
1552 }
1553 }
1554}
1555
1556impl<'a, TOwned, TBorrowed> PartialEq for MaybeOwned<'a, TOwned, TBorrowed>
1557where
1558 TOwned: Borrow<TBorrowed>,
1559 TBorrowed: PartialEq + ?Sized,
1560{
1561 fn eq(&self, other: &Self) -> bool {
1562 <Self as Borrow<TBorrowed>>::borrow(self).eq(other.borrow())
1563 }
1564}
1565
1566impl<'a, TOwned, TBorrowed> PartialOrd for MaybeOwned<'a, TOwned, TBorrowed>
1567where
1568 TOwned: Borrow<TBorrowed>,
1569 TBorrowed: PartialOrd + ?Sized,
1570{
1571 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1572 <Self as Borrow<TBorrowed>>::borrow(self).partial_cmp(other.borrow())
1573 }
1574}
1575
1576impl<'a, TOwned, TBorrowed> PartialEq<TBorrowed> for MaybeOwned<'a, TOwned, TBorrowed>
1577where
1578 TOwned: Borrow<TBorrowed>,
1579 TBorrowed: PartialEq + ?Sized,
1580{
1581 fn eq(&self, other: &TBorrowed) -> bool {
1582 <Self as Borrow<TBorrowed>>::borrow(self).eq(other)
1583 }
1584}
1585
1586impl<'a, TOwned, TBorrowed> PartialOrd<TBorrowed> for MaybeOwned<'a, TOwned, TBorrowed>
1587where
1588 TOwned: Borrow<TBorrowed>,
1589 TBorrowed: PartialOrd + ?Sized,
1590{
1591 fn partial_cmp(&self, other: &TBorrowed) -> Option<std::cmp::Ordering> {
1592 <Self as Borrow<TBorrowed>>::borrow(self).partial_cmp(other)
1593 }
1594}
1595
1596pub(crate) enum ListState<'a, Cn, Cl, PrimaryKey>
1597where
1598 Cl: schema::Collection,
1599 PrimaryKey: KeyEncoding<Cl::PrimaryKey> + PartialEq + ?Sized,
1600 Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
1601{
1602 Pending(Option<AsyncListBuilder<'a, Cn, Cl, PrimaryKey>>),
1603 Executing(BoxFuture<'a, Result<Vec<OwnedDocument>, Error>>),
1604}
1605
1606/// Retrieves a list of documents from a collection, when awaited. This
1607/// structure also offers functions to customize the options for the operation.
1608#[must_use]
1609pub struct AsyncList<'a, Cn, Cl, PrimaryKey>
1610where
1611 Cl: schema::Collection,
1612 PrimaryKey: KeyEncoding<Cl::PrimaryKey> + PartialEq + ?Sized,
1613 Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
1614{
1615 state: ListState<'a, Cn, Cl, PrimaryKey>,
1616}
1617
1618impl<'a, Cn, Cl, PrimaryKey> AsyncList<'a, Cn, Cl, PrimaryKey>
1619where
1620 Cl: schema::Collection,
1621 Cn: AsyncConnection,
1622 PrimaryKey: KeyEncoding<Cl::PrimaryKey> + PartialEq + ?Sized,
1623 Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
1624{
1625 pub(crate) const fn new(
1626 collection: MaybeOwned<'a, AsyncCollection<'a, Cn, Cl>>,
1627 range: RangeRef<'a, Cl::PrimaryKey, PrimaryKey>,
1628 ) -> Self {
1629 Self {
1630 state: ListState::Pending(Some(AsyncListBuilder {
1631 collection,
1632 range,
1633 sort: Sort::Ascending,
1634 limit: None,
1635 })),
1636 }
1637 }
1638
1639 fn builder(&mut self) -> &mut AsyncListBuilder<'a, Cn, Cl, PrimaryKey> {
1640 if let ListState::Pending(Some(builder)) = &mut self.state {
1641 builder
1642 } else {
1643 unreachable!("Attempted to use after retrieving the result")
1644 }
1645 }
1646
1647 /// Lists documents by id in ascending order.
1648 pub fn ascending(mut self) -> Self {
1649 self.builder().sort = Sort::Ascending;
1650 self
1651 }
1652
1653 /// Lists documents by id in descending order.
1654 pub fn descending(mut self) -> Self {
1655 self.builder().sort = Sort::Descending;
1656 self
1657 }
1658
1659 /// Sets the maximum number of results to return.
1660 pub fn limit(mut self, maximum_results: u32) -> Self {
1661 self.builder().limit = Some(maximum_results);
1662 self
1663 }
1664
1665 /// Returns the list of headers for documents contained within the range.
1666 ///
1667 /// ```rust
1668 /// # bonsaidb_core::__doctest_prelude!();
1669 /// # use bonsaidb_core::connection::AsyncConnection;
1670 /// # fn test_fn<C: AsyncConnection>(db: &C) -> Result<(), Error> {
1671 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1672 /// println!(
1673 /// "Number of documents with id 42 or larger: {:?}",
1674 /// db.collection::<MyCollection>().list(42..).headers().await?
1675 /// );
1676 /// println!(
1677 /// "Number of documents in MyCollection: {:?}",
1678 /// db.collection::<MyCollection>().all().headers().await?
1679 /// );
1680 /// # Ok(())
1681 /// # })
1682 /// # }
1683 /// ```
1684 pub async fn headers(self) -> Result<Vec<Header>, Error> {
1685 match self.state {
1686 ListState::Pending(Some(AsyncListBuilder {
1687 collection,
1688 range,
1689 sort,
1690 limit,
1691 ..
1692 })) => {
1693 collection
1694 .connection
1695 .list_headers::<Cl, _, _>(range, sort, limit)
1696 .await
1697 }
1698 _ => unreachable!("Attempted to use after retrieving the result"),
1699 }
1700 }
1701
1702 /// Returns the number of documents contained within the range.
1703 ///
1704 /// Order and limit are ignored if they were set.
1705 ///
1706 /// ```rust
1707 /// # bonsaidb_core::__doctest_prelude!();
1708 /// # use bonsaidb_core::connection::AsyncConnection;
1709 /// # fn test_fn<C: AsyncConnection>(db: &C) -> Result<(), Error> {
1710 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1711 /// println!(
1712 /// "Number of documents with id 42 or larger: {}",
1713 /// db.collection::<MyCollection>().list(42..).count().await?
1714 /// );
1715 /// println!(
1716 /// "Number of documents in MyCollection: {}",
1717 /// db.collection::<MyCollection>().all().count().await?
1718 /// );
1719 /// # Ok(())
1720 /// # })
1721 /// # }
1722 /// ```
1723 pub async fn count(self) -> Result<u64, Error> {
1724 match self.state {
1725 ListState::Pending(Some(AsyncListBuilder {
1726 collection, range, ..
1727 })) => collection.connection.count::<Cl, _, _>(range).await,
1728 _ => unreachable!("Attempted to use after retrieving the result"),
1729 }
1730 }
1731}
1732
1733#[allow(clippy::type_repetition_in_bounds)]
1734impl<'a, Cn, Cl, PrimaryKey> Future for AsyncList<'a, Cn, Cl, PrimaryKey>
1735where
1736 Cn: AsyncConnection,
1737 Cl: schema::Collection + Unpin,
1738 PrimaryKey: KeyEncoding<Cl::PrimaryKey> + PartialEq + ?Sized + Unpin,
1739 Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey> + Unpin,
1740{
1741 type Output = Result<Vec<OwnedDocument>, Error>;
1742
1743 fn poll(
1744 mut self: std::pin::Pin<&mut Self>,
1745 cx: &mut std::task::Context<'_>,
1746 ) -> std::task::Poll<Self::Output> {
1747 match &mut self.state {
1748 ListState::Executing(future) => future.as_mut().poll(cx),
1749 ListState::Pending(builder) => {
1750 let AsyncListBuilder {
1751 collection,
1752 range,
1753 sort,
1754 limit,
1755 } = builder.take().unwrap();
1756
1757 let future = async move {
1758 collection
1759 .connection
1760 .list::<Cl, _, _>(range, sort, limit)
1761 .await
1762 }
1763 .boxed();
1764
1765 self.state = ListState::Executing(future);
1766 self.poll(cx)
1767 }
1768 }
1769 }
1770}
1771
1772/// Parameters to query a [`schema::View`].
1773///
1774/// The examples for this type use this view definition:
1775///
1776/// ```rust
1777/// # mod collection {
1778/// # bonsaidb_core::__doctest_prelude!();
1779/// # }
1780/// # use collection::MyCollection;
1781/// use bonsaidb_core::define_basic_unique_mapped_view;
1782/// use bonsaidb_core::document::{CollectionDocument, Emit};
1783/// use bonsaidb_core::schema::{
1784/// CollectionMapReduce, DefaultViewSerialization, Name, ReduceResult, View, ViewMapResult,
1785/// ViewMappedValue, ViewSchema,
1786/// };
1787///
1788/// #[derive(Debug, Clone, View, ViewSchema)]
1789/// #[view(collection = MyCollection, key = u32, value = f32, name = "scores-by-rank")]
1790/// # #[view(core = bonsaidb_core)]
1791/// # #[view_schema(core = bonsaidb_core)]
1792/// pub struct ScoresByRank;
1793///
1794/// impl CollectionMapReduce for ScoresByRank {
1795/// fn map<'doc>(
1796/// &self,
1797/// document: CollectionDocument<<Self::View as View>::Collection>,
1798/// ) -> ViewMapResult<'doc, Self::View> {
1799/// document
1800/// .header
1801/// .emit_key_and_value(document.contents.rank, document.contents.score)
1802/// }
1803///
1804/// fn reduce(
1805/// &self,
1806/// mappings: &[ViewMappedValue<'_, Self::View>],
1807/// rereduce: bool,
1808/// ) -> ReduceResult<Self::View> {
1809/// if mappings.is_empty() {
1810/// Ok(0.)
1811/// } else {
1812/// Ok(mappings.iter().map(|map| map.value).sum::<f32>() / mappings.len() as f32)
1813/// }
1814/// }
1815/// }
1816/// ```
1817#[must_use]
1818pub struct AsyncView<'a, Cn, V: schema::SerializedView, Key>
1819where
1820 V::Key: Borrow<Key> + PartialEq<Key>,
1821 Key: PartialEq + ?Sized,
1822{
1823 connection: &'a Cn,
1824
1825 /// Key filtering criteria.
1826 pub key: Option<QueryKey<'a, V::Key, Key>>,
1827
1828 /// The view's data access policy. The default value is [`AccessPolicy::UpdateBefore`].
1829 pub access_policy: AccessPolicy,
1830
1831 /// The sort order of the query.
1832 pub sort: Sort,
1833
1834 /// The maximum number of results to return.
1835 pub limit: Option<u32>,
1836
1837 _view: PhantomData<V>,
1838}
1839
1840impl<'a, Cn, V, Key> AsyncView<'a, Cn, V, Key>
1841where
1842 V: schema::SerializedView,
1843 Cn: AsyncConnection,
1844 Key: KeyEncoding<V::Key> + PartialEq + ?Sized,
1845 V::Key: Borrow<Key> + PartialEq<Key>,
1846{
1847 const fn new(connection: &'a Cn) -> Self {
1848 Self {
1849 connection,
1850 key: None,
1851 access_policy: AccessPolicy::UpdateBefore,
1852 sort: Sort::Ascending,
1853 limit: None,
1854 _view: PhantomData,
1855 }
1856 }
1857
1858 /// Filters for entries in the view with `key`.
1859 ///
1860 /// ```rust
1861 /// # bonsaidb_core::__doctest_prelude!();
1862 /// # use bonsaidb_core::connection::AsyncConnection;
1863 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
1864 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1865 /// // score is an f32 in this example
1866 /// for mapping in ScoresByRank::entries_async(&db)
1867 /// .with_key(&42)
1868 /// .query()
1869 /// .await?
1870 /// {
1871 /// assert_eq!(mapping.key, 42);
1872 /// println!("Rank {} has a score of {:3}", mapping.key, mapping.value);
1873 /// }
1874 /// # Ok(())
1875 /// # })
1876 /// # }
1877 /// ```
1878 #[allow(clippy::missing_const_for_fn)] // false positive, destructors
1879 pub fn with_key<K>(self, key: &'a K) -> AsyncView<'a, Cn, V, K>
1880 where
1881 K: KeyEncoding<V::Key> + PartialEq + ?Sized,
1882 V::Key: Borrow<K> + PartialEq<K>,
1883 {
1884 AsyncView {
1885 connection: self.connection,
1886 key: Some(QueryKey::Matches(MaybeOwned::Borrowed(key))),
1887 access_policy: self.access_policy,
1888 sort: self.sort,
1889 limit: self.limit,
1890 _view: PhantomData,
1891 }
1892 }
1893
1894 /// Filters for entries in the view with `keys`.
1895 ///
1896 /// ```rust
1897 /// # bonsaidb_core::__doctest_prelude!();
1898 /// # use bonsaidb_core::connection::AsyncConnection;
1899 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
1900 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1901 /// // score is an f32 in this example
1902 /// for mapping in ScoresByRank::entries_async(&db)
1903 /// .with_keys(&[42, 43])
1904 /// .query()
1905 /// .await?
1906 /// {
1907 /// println!("Rank {} has a score of {:3}", mapping.key, mapping.value);
1908 /// }
1909 /// # Ok(())
1910 /// # })
1911 /// # }
1912 /// ```
1913 pub fn with_keys<K, IntoIter: IntoIterator<Item = &'a K>>(
1914 self,
1915 keys: IntoIter,
1916 ) -> AsyncView<'a, Cn, V, K>
1917 where
1918 K: PartialEq + ?Sized,
1919 V::Key: Borrow<K> + PartialEq<K>,
1920 {
1921 AsyncView {
1922 connection: self.connection,
1923 key: Some(QueryKey::Multiple(
1924 keys.into_iter().map(MaybeOwned::Borrowed).collect(),
1925 )),
1926 access_policy: self.access_policy,
1927 sort: self.sort,
1928 limit: self.limit,
1929 _view: PhantomData,
1930 }
1931 }
1932
1933 /// Filters for entries in the view with the range `keys`.
1934 ///
1935 /// ```rust
1936 /// # bonsaidb_core::__doctest_prelude!();
1937 /// # use bonsaidb_core::connection::AsyncConnection;
1938 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
1939 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1940 /// // score is an f32 in this example
1941 /// for mapping in ScoresByRank::entries_async(&db)
1942 /// .with_key_range(42..)
1943 /// .query()
1944 /// .await?
1945 /// {
1946 /// assert!(mapping.key >= 42);
1947 /// println!("Rank {} has a score of {:3}", mapping.key, mapping.value);
1948 /// }
1949 /// # Ok(())
1950 /// # })
1951 /// # }
1952 /// ```
1953 pub fn with_key_range<K, R: Into<RangeRef<'a, V::Key, K>>>(
1954 self,
1955 range: R,
1956 ) -> AsyncView<'a, Cn, V, K>
1957 where
1958 K: KeyEncoding<V::Key> + PartialEq + ?Sized,
1959 V::Key: Borrow<K> + PartialEq<K>,
1960 {
1961 AsyncView {
1962 connection: self.connection,
1963 key: Some(QueryKey::Range(range.into())),
1964 access_policy: self.access_policy,
1965 sort: self.sort,
1966 limit: self.limit,
1967 _view: PhantomData,
1968 }
1969 }
1970
1971 /// Filters for entries in the view with keys that begin with `prefix`.
1972 ///
1973 /// ```rust
1974 /// # bonsaidb_core::__doctest_prelude!();
1975 /// # use bonsaidb_core::connection::AsyncConnection;
1976 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
1977 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
1978 /// #[derive(View, Debug, Clone)]
1979 /// #[view(name = "by-name", key = String, collection = MyCollection)]
1980 /// # #[view(core = bonsaidb_core)]
1981 /// struct ByName;
1982 ///
1983 /// // score is an f32 in this example
1984 /// for mapping in ByName::entries_async(&db)
1985 /// .with_key_prefix("a")
1986 /// .query()
1987 /// .await?
1988 /// {
1989 /// assert!(mapping.key.starts_with("a"));
1990 /// println!("{} in document {:?}", mapping.key, mapping.source);
1991 /// }
1992 /// # Ok(())
1993 /// # })
1994 /// # }
1995 /// ```
1996 pub fn with_key_prefix<K>(self, prefix: &'a K) -> AsyncView<'a, Cn, V, K>
1997 where
1998 K: KeyEncoding<V::Key> + IntoPrefixRange<'a, V::Key> + PartialEq + ?Sized,
1999 V::Key: Borrow<K> + PartialEq<K>,
2000 {
2001 AsyncView {
2002 connection: self.connection,
2003 key: Some(QueryKey::Range(prefix.to_prefix_range())),
2004 access_policy: self.access_policy,
2005 sort: self.sort,
2006 limit: self.limit,
2007 _view: PhantomData,
2008 }
2009 }
2010
2011 /// Sets the access policy for queries.
2012 ///
2013 /// ```rust
2014 /// # bonsaidb_core::__doctest_prelude!();
2015 /// # use bonsaidb_core::connection::AsyncConnection;
2016 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
2017 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
2018 /// // score is an f32 in this example
2019 /// for mapping in ScoresByRank::entries_async(&db)
2020 /// .with_access_policy(AccessPolicy::UpdateAfter)
2021 /// .query()
2022 /// .await?
2023 /// {
2024 /// println!("Rank {} has a score of {:3}", mapping.key, mapping.value);
2025 /// }
2026 /// # Ok(())
2027 /// # })
2028 /// # }
2029 /// ```
2030 pub const fn with_access_policy(mut self, policy: AccessPolicy) -> Self {
2031 self.access_policy = policy;
2032 self
2033 }
2034
2035 /// Returns the matching mappings in ascending key order. This is the
2036 /// default sorting behavior.
2037 ///
2038 /// When more than one mapping exists for a single key, all matching
2039 /// mappings are returned as a unique entry. The resulting mappings are
2040 /// sorted only by the key, and as such, the order of mappings with the same
2041 /// key is undefined.
2042 ///
2043 /// ```rust
2044 /// # bonsaidb_core::__doctest_prelude!();
2045 /// # use bonsaidb_core::connection::AsyncConnection;
2046 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
2047 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
2048 /// // score is an f32 in this example
2049 /// for mapping in ScoresByRank::entries_async(&db).ascending().query().await? {
2050 /// println!("Rank {} has a score of {:3}", mapping.key, mapping.value);
2051 /// }
2052 /// # Ok(())
2053 /// # })
2054 /// # }
2055 /// ```
2056 pub const fn ascending(mut self) -> Self {
2057 self.sort = Sort::Ascending;
2058 self
2059 }
2060
2061 /// Returns the matching mappings in descending key order.
2062 ///
2063 /// When more than one mapping exists for a single key, all matching
2064 /// mappings are returned as a unique entry. The resulting mappings are
2065 /// sorted only by the key, and as such, the order of mappings with the same
2066 /// key is undefined.
2067 ///
2068 /// ```rust
2069 /// # bonsaidb_core::__doctest_prelude!();
2070 /// # use bonsaidb_core::connection::AsyncConnection;
2071 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
2072 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
2073 /// // score is an f32 in this example
2074 /// for mapping in ScoresByRank::entries_async(&db)
2075 /// .descending()
2076 /// .query()
2077 /// .await?
2078 /// {
2079 /// println!("Rank {} has a score of {:3}", mapping.key, mapping.value);
2080 /// }
2081 /// # Ok(())
2082 /// # })
2083 /// # }
2084 /// ```
2085 pub const fn descending(mut self) -> Self {
2086 self.sort = Sort::Descending;
2087 self
2088 }
2089
2090 /// Sets the maximum number of results to return.
2091 ///
2092 /// ```rust
2093 /// # bonsaidb_core::__doctest_prelude!();
2094 /// # use bonsaidb_core::connection::AsyncConnection;
2095 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
2096 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
2097 /// // score is an f32 in this example
2098 /// let mappings = ScoresByRank::entries_async(&db).limit(10).query().await?;
2099 /// assert!(mappings.len() <= 10);
2100 /// # Ok(())
2101 /// # })
2102 /// # }
2103 /// ```
2104 pub const fn limit(mut self, maximum_results: u32) -> Self {
2105 self.limit = Some(maximum_results);
2106 self
2107 }
2108
2109 /// Executes the query and retrieves the results.
2110 ///
2111 /// ```rust
2112 /// # bonsaidb_core::__doctest_prelude!();
2113 /// # use bonsaidb_core::connection::AsyncConnection;
2114 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
2115 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
2116 /// // score is an f32 in this example
2117 /// for mapping in ScoresByRank::entries_async(&db).query().await? {
2118 /// println!("Rank {} has a score of {:3}", mapping.key, mapping.value);
2119 /// }
2120 /// # Ok(())
2121 /// # })
2122 /// # }
2123 /// ```
2124 pub async fn query(self) -> Result<ViewMappingsCurrent<V>, Error> {
2125 self.connection
2126 .query::<V, Key>(self.key, self.sort, self.limit, self.access_policy)
2127 .await
2128 }
2129
2130 /// Executes the query and retrieves the results with the associated [`Document`s](crate::document::OwnedDocument).
2131 ///
2132 /// ```rust
2133 /// # bonsaidb_core::__doctest_prelude!();
2134 /// # use bonsaidb_core::connection::AsyncConnection;
2135 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
2136 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
2137 /// for mapping in &ScoresByRank::entries_async(&db)
2138 /// .with_key_range(42..=44)
2139 /// .query_with_docs()
2140 /// .await?
2141 /// {
2142 /// println!(
2143 /// "Mapping from #{} with rank: {} and score: {}. Document bytes: {:?}",
2144 /// mapping.document.header.id, mapping.key, mapping.value, mapping.document.contents
2145 /// );
2146 /// }
2147 /// # Ok(())
2148 /// # })
2149 /// # }
2150 /// ```
2151 pub async fn query_with_docs(self) -> Result<MappedDocuments<OwnedDocument, V>, Error> {
2152 self.connection
2153 .query_with_docs::<V, _>(self.key, self.sort, self.limit, self.access_policy)
2154 .await
2155 }
2156
2157 /// Executes the query and retrieves the results with the associated [`CollectionDocument`s](crate::document::CollectionDocument).
2158 ///
2159 /// ```rust
2160 /// # bonsaidb_core::__doctest_prelude!();
2161 /// # use bonsaidb_core::connection::AsyncConnection;
2162 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
2163 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
2164 /// for mapping in &ScoresByRank::entries_async(&db)
2165 /// .with_key_range(42..=44)
2166 /// .query_with_collection_docs()
2167 /// .await?
2168 /// {
2169 /// println!(
2170 /// "Mapping from #{} with rank: {} and score: {}. Deserialized Contents: {:?}",
2171 /// mapping.document.header.id, mapping.key, mapping.value, mapping.document.contents
2172 /// );
2173 /// }
2174 /// # Ok(())
2175 /// # })
2176 /// # }
2177 /// ```
2178 pub async fn query_with_collection_docs(
2179 self,
2180 ) -> Result<MappedDocuments<CollectionDocument<V::Collection>, V>, Error>
2181 where
2182 V::Collection: SerializedCollection,
2183 <V::Collection as SerializedCollection>::Contents: std::fmt::Debug,
2184 {
2185 self.connection
2186 .query_with_collection_docs::<V, _>(self.key, self.sort, self.limit, self.access_policy)
2187 .await
2188 }
2189
2190 /// Executes a reduce over the results of the query
2191 ///
2192 /// ```rust
2193 /// # bonsaidb_core::__doctest_prelude!();
2194 /// # use bonsaidb_core::connection::AsyncConnection;
2195 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
2196 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
2197 /// // score is an f32 in this example
2198 /// let score = ScoresByRank::entries_async(&db).reduce().await?;
2199 /// println!("Average score: {:3}", score);
2200 /// # Ok(())
2201 /// # })
2202 /// # }
2203 /// ```
2204 pub async fn reduce(self) -> Result<V::Value, Error> {
2205 self.connection
2206 .reduce::<V, _>(self.key, self.access_policy)
2207 .await
2208 }
2209
2210 /// Executes a reduce over the results of the query, grouping by key.
2211 ///
2212 /// ```rust
2213 /// # bonsaidb_core::__doctest_prelude!();
2214 /// # use bonsaidb_core::connection::AsyncConnection;
2215 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
2216 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
2217 /// // score is an f32 in this example
2218 /// for mapping in ScoresByRank::entries_async(&db).reduce_grouped().await? {
2219 /// println!(
2220 /// "Rank {} has an average score of {:3}",
2221 /// mapping.key, mapping.value
2222 /// );
2223 /// }
2224 /// # Ok(())
2225 /// # })
2226 /// # }
2227 /// ```
2228 pub async fn reduce_grouped(self) -> Result<Vec<MappedValue<V::Key, V::Value>>, Error> {
2229 self.connection
2230 .reduce_grouped::<V, _>(self.key, self.access_policy)
2231 .await
2232 }
2233
2234 /// Deletes all of the associated documents that match this view query.
2235 ///
2236 /// ```rust
2237 /// # bonsaidb_core::__doctest_prelude!();
2238 /// # use bonsaidb_core::connection::AsyncConnection;
2239 /// # fn test_fn<C: AsyncConnection>(db: C) -> Result<(), Error> {
2240 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
2241 /// ScoresByRank::entries_async(&db).delete_docs().await?;
2242 /// # Ok(())
2243 /// # })
2244 /// # }
2245 /// ```
2246 pub async fn delete_docs(self) -> Result<u64, Error> {
2247 self.connection
2248 .delete_docs::<V, _>(self.key, self.access_policy)
2249 .await
2250 }
2251}
2252
2253/// A sort order.
2254#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
2255pub enum Sort {
2256 /// Sort ascending (A -> Z).
2257 Ascending,
2258 /// Sort descending (Z -> A).
2259 Descending,
2260}
2261
2262/// Filters a [`View`] by key.
2263#[derive(Clone, Debug)]
2264pub enum QueryKey<'k, KOwned, KBorrowed = KOwned>
2265where
2266 KBorrowed: PartialEq + ?Sized,
2267 KOwned: Borrow<KBorrowed> + PartialEq<KBorrowed>,
2268{
2269 /// Matches all entries with the key provided.
2270 Matches(MaybeOwned<'k, KOwned, KBorrowed>),
2271
2272 /// Matches all entires with keys in the range provided.
2273 Range(RangeRef<'k, KOwned, KBorrowed>),
2274
2275 /// Matches all entries that have keys that are included in the set provided.
2276 Multiple(Vec<MaybeOwned<'k, KOwned, KBorrowed>>),
2277}
2278
2279impl<'a, KOwned, KBorrowed> QueryKey<'a, KOwned, KBorrowed>
2280where
2281 KBorrowed: KeyEncoding<KOwned> + PartialEq + ?Sized,
2282 KOwned: for<'k> Key<'k> + Borrow<KBorrowed> + PartialEq<KBorrowed>,
2283{
2284 /// Converts this key to a serialized format using the [`Key`] trait.
2285 pub fn serialized(&self) -> Result<SerializedQueryKey, Error> {
2286 match self {
2287 Self::Matches(key) => key
2288 .as_ord_bytes()
2289 .map_err(|err| Error::other("key serialization", err))
2290 .map(|v| SerializedQueryKey::Matches(Bytes::from(v.to_vec()))),
2291 Self::Range(range) => Ok(SerializedQueryKey::Range(
2292 range
2293 .as_ord_bytes()
2294 .map_err(|err| Error::other("key serialization", err))?,
2295 )),
2296 Self::Multiple(keys) => {
2297 let keys = keys
2298 .iter()
2299 .map(|key| {
2300 key.as_ord_bytes()
2301 .map(|key| Bytes::from(key.to_vec()))
2302 .map_err(|err| Error::other("key serialization", err))
2303 })
2304 .collect::<Result<Vec<_>, Error>>()?;
2305
2306 Ok(SerializedQueryKey::Multiple(keys))
2307 }
2308 }
2309 }
2310}
2311
2312/// A [`QueryKey`] that has had its keys serialized.
2313#[derive(Clone, Serialize, Deserialize, Debug)]
2314pub enum SerializedQueryKey {
2315 /// Matches all entries with the key provided.
2316 Matches(Bytes),
2317
2318 /// Matches all entires with keys in the range provided.
2319 Range(Range<Bytes>),
2320
2321 /// Matches all entries that have keys that are included in the set provided.
2322 Multiple(Vec<Bytes>),
2323}
2324
2325impl SerializedQueryKey {
2326 /// Deserializes the bytes into `K` via the [`Key`] trait.
2327 pub fn deserialized<K: for<'k> Key<'k> + PartialEq>(
2328 &self,
2329 ) -> Result<QueryKey<'static, K>, Error> {
2330 match self {
2331 Self::Matches(key) => K::from_ord_bytes(ByteSource::Borrowed(key.as_ref()))
2332 .map_err(|err| Error::other("key serialization", err))
2333 .map(|key| QueryKey::Matches(MaybeOwned::Owned(key))),
2334 Self::Range(range) => Ok(QueryKey::Range(RangeRef::owned(
2335 range
2336 .deserialize()
2337 .map_err(|err| Error::other("key serialization", err))?,
2338 ))),
2339 Self::Multiple(keys) => {
2340 let keys = keys
2341 .iter()
2342 .map(|key| {
2343 K::from_ord_bytes(ByteSource::Borrowed(key.as_ref()))
2344 .map(MaybeOwned::Owned)
2345 .map_err(|err| Error::other("key serialization", err))
2346 })
2347 .collect::<Result<Vec<_>, Error>>()?;
2348
2349 Ok(QueryKey::Multiple(keys))
2350 }
2351 }
2352 }
2353}
2354
2355/// A range type that can represent all `std` range types and be serialized.
2356///
2357/// This type implements conversion operations from all range types defined in
2358/// `std`.
2359#[derive(Serialize, Deserialize, Default, Debug, Copy, Clone, Eq, PartialEq)]
2360#[must_use]
2361pub struct Range<T> {
2362 /// The start of the range.
2363 pub start: Bound<T>,
2364 /// The end of the range.
2365 pub end: Bound<T>,
2366}
2367
2368/// A range bound that can be serialized.
2369#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq)]
2370#[must_use]
2371pub enum Bound<T> {
2372 /// No bound.
2373 Unbounded,
2374 /// Bounded by the contained value (inclusive).
2375 Included(T),
2376 /// Bounded by the contained value (exclusive).
2377 Excluded(T),
2378}
2379
2380impl<T> Default for Bound<T> {
2381 fn default() -> Self {
2382 Self::Unbounded
2383 }
2384}
2385
2386impl<T> Range<T> {
2387 /// Sets the start bound of this range to [`Bound::Excluded`] with
2388 /// `excluded_start`. The range will represent values that are
2389 /// [`Ordering::Greater`](std::cmp::Ordering::Greater) than, but not
2390 /// including, `excluded_start`.
2391 #[allow(clippy::missing_const_for_fn)]
2392 pub fn after(mut self, excluded_start: T) -> Self {
2393 self.start = Bound::Excluded(excluded_start);
2394 self
2395 }
2396
2397 /// Sets the start bound of this range to [`Bound::Included`] with
2398 /// `included_start`. The range will represent values that are
2399 /// [`Ordering::Greater`](std::cmp::Ordering::Greater) than or
2400 /// [`Ordering::Equal`](std::cmp::Ordering::Equal) to `included_start`.
2401 #[allow(clippy::missing_const_for_fn)]
2402 pub fn start_at(mut self, included_start: T) -> Self {
2403 self.start = Bound::Included(included_start);
2404 self
2405 }
2406
2407 /// Sets the end bound of this range to [`Bound::Excluded`] with
2408 /// `excluded_end`. The range will represent values that are
2409 /// [`Ordering::Less`](std::cmp::Ordering::Less) than, but not including,
2410 /// `excluded_end`.
2411 #[allow(clippy::missing_const_for_fn)]
2412 pub fn before(mut self, excluded_end: T) -> Self {
2413 self.end = Bound::Excluded(excluded_end);
2414 self
2415 }
2416
2417 /// Sets the end bound of this range to [`Bound::Included`] with
2418 /// `included_end`. The range will represent values that are
2419 /// [`Ordering:::Less`](std::cmp::Ordering::Less) than or
2420 /// [`Ordering::Equal`](std::cmp::Ordering::Equal) to `included_end`.
2421 #[allow(clippy::missing_const_for_fn)]
2422 pub fn end_at(mut self, included_end: T) -> Self {
2423 self.end = Bound::Included(included_end);
2424 self
2425 }
2426
2427 /// Maps each contained value with the function provided.
2428 pub fn map<U, F: Fn(T) -> U>(self, map: F) -> Range<U> {
2429 Range {
2430 start: self.start.map(&map),
2431 end: self.end.map(&map),
2432 }
2433 }
2434
2435 /// Maps each contained value with the function provided. The callback's
2436 /// return type is a Result, unlike with `map`.
2437 pub fn map_result<U, E, F: Fn(T) -> Result<U, E>>(self, map: F) -> Result<Range<U>, E> {
2438 Ok(Range {
2439 start: self.start.map_result(&map)?,
2440 end: self.end.map_result(&map)?,
2441 })
2442 }
2443
2444 /// Maps each contained value as a reference.
2445 pub fn map_ref<U: ?Sized, F: Fn(&T) -> &U>(&self, map: F) -> Range<&U> {
2446 Range {
2447 start: self.start.map_ref(&map),
2448 end: self.end.map_ref(&map),
2449 }
2450 }
2451}
2452
2453#[test]
2454fn range_constructors() {
2455 assert_eq!(
2456 Range::default().after(1_u32),
2457 Range {
2458 start: Bound::Excluded(1),
2459 end: Bound::Unbounded
2460 }
2461 );
2462 assert_eq!(
2463 Range::default().start_at(1_u32),
2464 Range {
2465 start: Bound::Included(1),
2466 end: Bound::Unbounded
2467 }
2468 );
2469 assert_eq!(
2470 Range::default().before(1_u32),
2471 Range {
2472 start: Bound::Unbounded,
2473 end: Bound::Excluded(1),
2474 }
2475 );
2476 assert_eq!(
2477 Range::default().end_at(1_u32),
2478 Range {
2479 start: Bound::Unbounded,
2480 end: Bound::Included(1),
2481 }
2482 );
2483}
2484
2485impl<'a, TOwned, TBorrowed> RangeRef<'a, TOwned, TBorrowed>
2486where
2487 TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
2488 TBorrowed: PartialEq + ?Sized,
2489{
2490 /// Serializes the range's contained values to big-endian bytes.
2491 pub fn as_ord_bytes(&'a self) -> Result<Range<Bytes>, TBorrowed::Error>
2492 where
2493 TBorrowed: KeyEncoding<TOwned>,
2494 TOwned: for<'k> Key<'k> + Borrow<TBorrowed>,
2495 {
2496 Ok(Range {
2497 start: self.start.as_ord_bytes()?,
2498 end: self.end.as_ord_bytes()?,
2499 })
2500 }
2501}
2502
2503impl Range<Bytes> {
2504 /// Deserializes the range's contained values from big-endian bytes.
2505 pub fn deserialize<T: for<'k> Key<'k>>(
2506 &self,
2507 ) -> Result<Range<T>, <T as KeyEncoding<T>>::Error> {
2508 Ok(Range {
2509 start: self.start.deserialize()?,
2510 end: self.start.deserialize()?,
2511 })
2512 }
2513}
2514
2515impl<T> Bound<T> {
2516 /// Maps the contained value, if any, and returns the resulting `Bound`.
2517 pub fn map<U, F: Fn(T) -> U>(self, map: F) -> Bound<U> {
2518 match self {
2519 Self::Unbounded => Bound::Unbounded,
2520 Self::Included(value) => Bound::Included(map(value)),
2521 Self::Excluded(value) => Bound::Excluded(map(value)),
2522 }
2523 }
2524
2525 /// Maps the contained value with the function provided. The callback's
2526 /// return type is a Result, unlike with `map`.
2527 pub fn map_result<U, E, F: Fn(T) -> Result<U, E>>(self, map: F) -> Result<Bound<U>, E> {
2528 Ok(match self {
2529 Self::Unbounded => Bound::Unbounded,
2530 Self::Included(value) => Bound::Included(map(value)?),
2531 Self::Excluded(value) => Bound::Excluded(map(value)?),
2532 })
2533 }
2534
2535 /// Maps each contained value as a reference.
2536 pub fn map_ref<U: ?Sized, F: Fn(&T) -> &U>(&self, map: F) -> Bound<&U> {
2537 match self {
2538 Self::Unbounded => Bound::Unbounded,
2539 Self::Included(value) => Bound::Included(map(value)),
2540 Self::Excluded(value) => Bound::Excluded(map(value)),
2541 }
2542 }
2543}
2544
2545impl<'a, TOwned, TBorrowed> BoundRef<'a, TOwned, TBorrowed>
2546where
2547 TBorrowed: PartialEq + ?Sized,
2548 TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
2549{
2550 /// Serializes the contained value to big-endian bytes.
2551 pub fn as_ord_bytes(&'a self) -> Result<Bound<Bytes>, TBorrowed::Error>
2552 where
2553 TBorrowed: KeyEncoding<TOwned>,
2554 TOwned: for<'k> Key<'k> + Borrow<TBorrowed>,
2555 {
2556 match self {
2557 Self::Unbounded => Ok(Bound::Unbounded),
2558 Self::Included(value) => {
2559 Ok(Bound::Included(Bytes::from(value.as_ord_bytes()?.to_vec())))
2560 }
2561 Self::Excluded(value) => {
2562 Ok(Bound::Excluded(Bytes::from(value.as_ord_bytes()?.to_vec())))
2563 }
2564 }
2565 }
2566}
2567
2568impl Bound<Bytes> {
2569 /// Deserializes the bound's contained value from big-endian bytes.
2570 pub fn deserialize<T: for<'k> Key<'k>>(
2571 &self,
2572 ) -> Result<Bound<T>, <T as KeyEncoding<T>>::Error> {
2573 match self {
2574 Self::Unbounded => Ok(Bound::Unbounded),
2575 Self::Included(value) => Ok(Bound::Included(T::from_ord_bytes(ByteSource::Borrowed(
2576 value.as_ref(),
2577 ))?)),
2578 Self::Excluded(value) => Ok(Bound::Excluded(T::from_ord_bytes(ByteSource::Borrowed(
2579 value.as_ref(),
2580 ))?)),
2581 }
2582 }
2583}
2584
2585impl<T> std::ops::RangeBounds<T> for Range<T> {
2586 fn start_bound(&self) -> std::ops::Bound<&T> {
2587 std::ops::Bound::from(&self.start)
2588 }
2589
2590 fn end_bound(&self) -> std::ops::Bound<&T> {
2591 std::ops::Bound::from(&self.end)
2592 }
2593}
2594
2595impl<'a, T> From<&'a Bound<T>> for std::ops::Bound<&'a T> {
2596 fn from(bound: &'a Bound<T>) -> Self {
2597 match bound {
2598 Bound::Unbounded => std::ops::Bound::Unbounded,
2599 Bound::Included(value) => std::ops::Bound::Included(value),
2600 Bound::Excluded(value) => std::ops::Bound::Excluded(value),
2601 }
2602 }
2603}
2604
2605impl<'a, T> From<Bound<&'a T>> for std::ops::Bound<&'a T> {
2606 fn from(bound: Bound<&'a T>) -> Self {
2607 match bound {
2608 Bound::Unbounded => std::ops::Bound::Unbounded,
2609 Bound::Included(value) => std::ops::Bound::Included(value),
2610 Bound::Excluded(value) => std::ops::Bound::Excluded(value),
2611 }
2612 }
2613}
2614
2615impl<T> From<std::ops::Range<T>> for Range<T> {
2616 fn from(range: std::ops::Range<T>) -> Self {
2617 Self {
2618 start: Bound::Included(range.start),
2619 end: Bound::Excluded(range.end),
2620 }
2621 }
2622}
2623
2624impl<T> From<std::ops::RangeFrom<T>> for Range<T> {
2625 fn from(range: std::ops::RangeFrom<T>) -> Self {
2626 Self {
2627 start: Bound::Included(range.start),
2628 end: Bound::Unbounded,
2629 }
2630 }
2631}
2632
2633impl<T> From<std::ops::RangeTo<T>> for Range<T> {
2634 fn from(range: std::ops::RangeTo<T>) -> Self {
2635 Self {
2636 start: Bound::Unbounded,
2637 end: Bound::Excluded(range.end),
2638 }
2639 }
2640}
2641
2642impl<T: Clone> From<std::ops::RangeInclusive<T>> for Range<T> {
2643 fn from(range: std::ops::RangeInclusive<T>) -> Self {
2644 Self {
2645 start: Bound::Included(range.start().clone()),
2646 end: Bound::Included(range.end().clone()),
2647 }
2648 }
2649}
2650
2651impl<T> From<std::ops::RangeToInclusive<T>> for Range<T> {
2652 fn from(range: std::ops::RangeToInclusive<T>) -> Self {
2653 Self {
2654 start: Bound::Unbounded,
2655 end: Bound::Included(range.end),
2656 }
2657 }
2658}
2659
2660impl<T> From<std::ops::RangeFull> for Range<T> {
2661 fn from(_: std::ops::RangeFull) -> Self {
2662 Self {
2663 start: Bound::Unbounded,
2664 end: Bound::Unbounded,
2665 }
2666 }
2667}
2668
2669/// A range reference type that can be serialized.
2670#[derive(Debug, Clone, PartialEq)]
2671#[must_use]
2672pub struct RangeRef<'a, TOwned, TBorrowed = TOwned>
2673where
2674 TBorrowed: PartialEq + ?Sized,
2675 TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
2676{
2677 /// The start of the range.
2678 pub start: BoundRef<'a, TOwned, TBorrowed>,
2679 /// The end of the range.
2680 pub end: BoundRef<'a, TOwned, TBorrowed>,
2681}
2682
2683impl<'a, TOwned, TBorrowed> From<std::ops::Range<TOwned>> for RangeRef<'a, TOwned, TBorrowed>
2684where
2685 TBorrowed: PartialEq,
2686 TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
2687{
2688 fn from(range: std::ops::Range<TOwned>) -> Self {
2689 Self {
2690 start: BoundRef::Included(MaybeOwned::Owned(range.start)),
2691 end: BoundRef::Excluded(MaybeOwned::Owned(range.end)),
2692 }
2693 }
2694}
2695
2696impl<'a, 'b, TOwned, TBorrowed> From<&'b std::ops::Range<&'a TBorrowed>>
2697 for RangeRef<'a, TOwned, TBorrowed>
2698where
2699 TBorrowed: PartialEq,
2700 TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
2701{
2702 fn from(range: &'b std::ops::Range<&'a TBorrowed>) -> Self {
2703 Self {
2704 start: BoundRef::Included(MaybeOwned::Borrowed(range.start)),
2705 end: BoundRef::Excluded(MaybeOwned::Borrowed(range.end)),
2706 }
2707 }
2708}
2709
2710impl<'a, TOwned, TBorrowed> From<std::ops::RangeFrom<TOwned>> for RangeRef<'a, TOwned, TBorrowed>
2711where
2712 TBorrowed: PartialEq,
2713 TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
2714{
2715 fn from(range: std::ops::RangeFrom<TOwned>) -> Self {
2716 Self {
2717 start: BoundRef::Included(MaybeOwned::Owned(range.start)),
2718 end: BoundRef::Unbounded,
2719 }
2720 }
2721}
2722
2723impl<'a, 'b, TOwned, TBorrowed> From<&'b std::ops::RangeFrom<&'a TBorrowed>>
2724 for RangeRef<'a, TOwned, TBorrowed>
2725where
2726 TBorrowed: PartialEq,
2727 TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
2728{
2729 fn from(range: &'b std::ops::RangeFrom<&'a TBorrowed>) -> Self {
2730 Self {
2731 start: BoundRef::Included(MaybeOwned::Borrowed(range.start)),
2732 end: BoundRef::Unbounded,
2733 }
2734 }
2735}
2736
2737impl<'a, TOwned, TBorrowed> From<std::ops::RangeTo<TOwned>> for RangeRef<'a, TOwned, TBorrowed>
2738where
2739 TBorrowed: PartialEq,
2740 TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
2741{
2742 fn from(range: std::ops::RangeTo<TOwned>) -> Self {
2743 Self {
2744 start: BoundRef::Unbounded,
2745 end: BoundRef::Excluded(MaybeOwned::Owned(range.end)),
2746 }
2747 }
2748}
2749
2750impl<'a, 'b, TOwned, TBorrowed> From<&'b std::ops::RangeTo<&'a TBorrowed>>
2751 for RangeRef<'a, TOwned, TBorrowed>
2752where
2753 TBorrowed: PartialEq,
2754 TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
2755{
2756 fn from(range: &'b std::ops::RangeTo<&'a TBorrowed>) -> Self {
2757 Self {
2758 start: BoundRef::Unbounded,
2759 end: BoundRef::Excluded(MaybeOwned::Borrowed(range.end)),
2760 }
2761 }
2762}
2763
2764impl<'a, TOwned, TBorrowed> From<std::ops::RangeInclusive<TOwned>>
2765 for RangeRef<'a, TOwned, TBorrowed>
2766where
2767 TBorrowed: PartialEq,
2768 TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed> + Clone,
2769{
2770 fn from(range: std::ops::RangeInclusive<TOwned>) -> Self {
2771 Self {
2772 start: BoundRef::Included(MaybeOwned::Owned(range.start().clone())),
2773 end: BoundRef::Included(MaybeOwned::Owned(range.end().clone())),
2774 }
2775 }
2776}
2777
2778impl<'a, 'b, TOwned, TBorrowed> From<&'b std::ops::RangeInclusive<&'a TBorrowed>>
2779 for RangeRef<'a, TOwned, TBorrowed>
2780where
2781 TBorrowed: PartialEq,
2782 TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
2783{
2784 fn from(range: &'b std::ops::RangeInclusive<&'a TBorrowed>) -> Self {
2785 Self {
2786 start: BoundRef::Included(MaybeOwned::Borrowed(range.start())),
2787 end: BoundRef::Included(MaybeOwned::Borrowed(range.end())),
2788 }
2789 }
2790}
2791
2792impl<'a, TOwned, TBorrowed> From<std::ops::RangeToInclusive<&'a TBorrowed>>
2793 for RangeRef<'a, TOwned, TBorrowed>
2794where
2795 TBorrowed: PartialEq,
2796 TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
2797{
2798 fn from(range: std::ops::RangeToInclusive<&'a TBorrowed>) -> Self {
2799 Self {
2800 start: BoundRef::Unbounded,
2801 end: BoundRef::Included(MaybeOwned::Borrowed(range.end)),
2802 }
2803 }
2804}
2805
2806impl<'a, TOwned, TBorrowed> From<std::ops::RangeFull> for RangeRef<'a, TOwned, TBorrowed>
2807where
2808 TBorrowed: PartialEq,
2809 TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
2810{
2811 fn from(_: std::ops::RangeFull) -> Self {
2812 Self {
2813 start: BoundRef::Unbounded,
2814 end: BoundRef::Unbounded,
2815 }
2816 }
2817}
2818
2819impl<'a, TOwned, TBorrowed> RangeRef<'a, TOwned, TBorrowed>
2820where
2821 TBorrowed: PartialEq + ?Sized,
2822 TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
2823{
2824 /// Returns a borrowed range ref using the bounds in the range provided.
2825 pub const fn borrowed(range: Range<&'a TBorrowed>) -> Self {
2826 Self {
2827 start: BoundRef::borrowed(range.start),
2828 end: BoundRef::borrowed(range.end),
2829 }
2830 }
2831
2832 /// Returns an owned range ref using the bounds in the range provided.
2833 pub fn owned(range: Range<TOwned>) -> Self {
2834 Self {
2835 start: BoundRef::owned(range.start),
2836 end: BoundRef::owned(range.end),
2837 }
2838 }
2839
2840 /// Maps each contained value with the function provided. The callback's
2841 /// return type is a Result, unlike with `map`.
2842 pub fn map_result<U, E, F: Fn(&TBorrowed) -> Result<U, E>>(
2843 self,
2844 map: F,
2845 ) -> Result<Range<U>, E> {
2846 Ok(Range {
2847 start: self.start.map_result(&map)?,
2848 end: self.end.map_result(&map)?,
2849 })
2850 }
2851}
2852
2853impl<'a, TOwned, TBorrowed> std::ops::RangeBounds<TBorrowed> for RangeRef<'a, TOwned, TBorrowed>
2854where
2855 TBorrowed: PartialEq + ?Sized,
2856 TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
2857{
2858 fn start_bound(&self) -> std::ops::Bound<&TBorrowed> {
2859 std::ops::Bound::from(&self.start)
2860 }
2861
2862 fn end_bound(&self) -> std::ops::Bound<&TBorrowed> {
2863 std::ops::Bound::from(&self.end)
2864 }
2865}
2866
2867impl<'a, TOwned, TBorrowed> From<&'a BoundRef<'a, TOwned, TBorrowed>>
2868 for std::ops::Bound<&'a TBorrowed>
2869where
2870 TBorrowed: PartialEq + ?Sized,
2871 TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
2872{
2873 fn from(bound: &'a BoundRef<'a, TOwned, TBorrowed>) -> Self {
2874 match bound {
2875 BoundRef::Unbounded => std::ops::Bound::Unbounded,
2876 BoundRef::Included(value) => std::ops::Bound::Included(value),
2877 BoundRef::Excluded(value) => std::ops::Bound::Excluded(value),
2878 }
2879 }
2880}
2881
2882/// A range bound reference.
2883#[derive(Debug, Clone, PartialEq)]
2884#[must_use]
2885pub enum BoundRef<'a, TOwned, TBorrowed = TOwned>
2886where
2887 TBorrowed: PartialEq + ?Sized,
2888 TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
2889 MaybeOwned<'a, TOwned, TBorrowed>: PartialEq,
2890{
2891 /// No bound.
2892 Unbounded,
2893 /// Bounded by the contained value (inclusive).
2894 Included(MaybeOwned<'a, TOwned, TBorrowed>),
2895 /// Bounded by the contained value (exclusive).
2896 Excluded(MaybeOwned<'a, TOwned, TBorrowed>),
2897}
2898
2899impl<'a, TOwned, TBorrowed> BoundRef<'a, TOwned, TBorrowed>
2900where
2901 TBorrowed: PartialEq + ?Sized,
2902 TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
2903{
2904 /// Returns a borrowed bound from the bound provided.
2905 pub const fn borrowed(range: Bound<&'a TBorrowed>) -> Self {
2906 match range {
2907 Bound::Unbounded => Self::Unbounded,
2908 Bound::Included(value) => Self::Included(MaybeOwned::Borrowed(value)),
2909 Bound::Excluded(value) => Self::Excluded(MaybeOwned::Borrowed(value)),
2910 }
2911 }
2912
2913 /// Returns an owned bound ref from the bound provided.
2914 #[allow(clippy::missing_const_for_fn)] // false positive, destructors
2915 pub fn owned(range: Bound<TOwned>) -> Self {
2916 match range {
2917 Bound::Unbounded => Self::Unbounded,
2918 Bound::Included(value) => Self::Included(MaybeOwned::Owned(value)),
2919 Bound::Excluded(value) => Self::Excluded(MaybeOwned::Owned(value)),
2920 }
2921 }
2922
2923 /// Maps each contained value with the function provided. The callback's
2924 /// return type is a Result, unlike with `map`.
2925 pub fn map_result<U, E, F: Fn(&TBorrowed) -> Result<U, E>>(
2926 self,
2927 map: F,
2928 ) -> Result<Bound<U>, E> {
2929 Ok(match self {
2930 BoundRef::Unbounded => Bound::Unbounded,
2931 BoundRef::Included(value) => Bound::Included(map(&*value)?),
2932 BoundRef::Excluded(value) => Bound::Excluded(map(&*value)?),
2933 })
2934 }
2935}
2936
2937/// Changes how the view's outdated data will be treated.
2938#[derive(Copy, Clone, Serialize, Deserialize, Debug)]
2939pub enum AccessPolicy {
2940 /// Update any changed documents before returning a response.
2941 UpdateBefore,
2942
2943 /// Return the results, which may be out-of-date, and start an update job in
2944 /// the background. This pattern is useful when you want to ensure you
2945 /// provide consistent response times while ensuring the database is
2946 /// updating in the background.
2947 UpdateAfter,
2948
2949 /// Returns the results, which may be out-of-date, and do not start any
2950 /// background jobs. This mode is useful if you're using a view as a cache
2951 /// and have a background process that is responsible for controlling when
2952 /// data is refreshed and updated. While the default `UpdateBefore`
2953 /// shouldn't have much overhead, this option removes all overhead related
2954 /// to view updating from the query.
2955 NoUpdate,
2956}
2957
2958/// Functions for interacting with a multi-database BonsaiDb instance.
2959#[async_trait]
2960pub trait StorageConnection: HasSession + Sized + Send + Sync {
2961 /// The type that represents a database for this implementation.
2962 type Database: Connection;
2963 /// The [`StorageConnection`] type returned from authentication calls.
2964 type Authenticated: StorageConnection;
2965
2966 /// Returns the administration database.
2967 fn admin(&self) -> Self::Database;
2968
2969 /// Creates a database named `name` with the `Schema` provided.
2970 ///
2971 /// ## Errors
2972 ///
2973 /// * [`Error::InvalidDatabaseName`]: `name` must begin with an alphanumeric
2974 /// character (`[a-zA-Z0-9]`), and all remaining characters must be
2975 /// alphanumeric, a period (`.`), or a hyphen (`-`).
2976 /// * [`Error::DatabaseNameAlreadyTaken`]: `name` was already used for a
2977 /// previous database name. Returned if `only_if_needed` is false.
2978 fn create_database<DB: Schema>(
2979 &self,
2980 name: &str,
2981 only_if_needed: bool,
2982 ) -> Result<Self::Database, crate::Error> {
2983 self.create_database_with_schema(name, DB::schema_name(), only_if_needed)?;
2984 self.database::<DB>(name)
2985 }
2986
2987 /// Returns a reference to database `name` with schema `DB`.
2988 fn database<DB: Schema>(&self, name: &str) -> Result<Self::Database, crate::Error>;
2989
2990 /// Creates a database named `name` using the [`SchemaName`] `schema`.
2991 ///
2992 /// ## Errors
2993 ///
2994 /// * [`Error::InvalidDatabaseName`]: `name` must begin with an alphanumeric
2995 /// character (`[a-zA-Z0-9]`), and all remaining characters must be
2996 /// alphanumeric, a period (`.`), or a hyphen (`-`).
2997 /// * [`Error::DatabaseNameAlreadyTaken`]: `name` was already used for a
2998 /// previous database name. Returned if `only_if_needed` is false.
2999 fn create_database_with_schema(
3000 &self,
3001 name: &str,
3002 schema: SchemaName,
3003 only_if_needed: bool,
3004 ) -> Result<(), crate::Error>;
3005
3006 /// Deletes a database named `name`.
3007 ///
3008 /// ## Errors
3009 ///
3010 /// * [`Error::DatabaseNotFound`]: database `name` does not exist.
3011 /// * [`Error::Other`]: an error occurred while deleting files.
3012 fn delete_database(&self, name: &str) -> Result<(), crate::Error>;
3013
3014 /// Lists the databases in this storage.
3015 fn list_databases(&self) -> Result<Vec<Database>, crate::Error>;
3016
3017 /// Lists the [`SchemaName`]s registered with this storage.
3018 fn list_available_schemas(&self) -> Result<Vec<SchemaSummary>, crate::Error>;
3019
3020 /// Creates a user.
3021 fn create_user(&self, username: &str) -> Result<u64, crate::Error>;
3022
3023 /// Deletes a user.
3024 fn delete_user<'user, U: Nameable<'user, u64> + Send + Sync>(
3025 &self,
3026 user: U,
3027 ) -> Result<(), crate::Error>;
3028
3029 /// Sets a user's password.
3030 #[cfg(feature = "password-hashing")]
3031 fn set_user_password<'user, U: Nameable<'user, u64> + Send + Sync>(
3032 &self,
3033 user: U,
3034 password: SensitiveString,
3035 ) -> Result<(), crate::Error>;
3036
3037 /// Authenticates using the active session, returning a connection with a
3038 /// new session upon success. The existing connection will remain usable
3039 /// with the existing authentication, if any.
3040 #[cfg(any(feature = "token-authentication", feature = "password-hashing"))]
3041 fn authenticate(
3042 &self,
3043 authentication: Authentication,
3044 ) -> Result<Self::Authenticated, crate::Error>;
3045
3046 /// Assumes the `identity`. If successful, the returned instance will have
3047 /// the permissions from `identity`.
3048 fn assume_identity(
3049 &self,
3050 identity: IdentityReference<'_>,
3051 ) -> Result<Self::Authenticated, crate::Error>;
3052
3053 /// Authenticates using an
3054 /// [`AuthenticationToken`](crate::admin::AuthenticationToken). If
3055 /// successful, the returned instance will have the permissions from
3056 /// `identity`.
3057 #[cfg(feature = "token-authentication")]
3058 fn authenticate_with_token(
3059 &self,
3060 id: u64,
3061 token: &SensitiveString,
3062 ) -> Result<<Self::Authenticated as StorageConnection>::Authenticated, crate::Error> {
3063 let challenge_session = self.authenticate(Authentication::token(id, token)?)?;
3064 match challenge_session
3065 .session()
3066 .map(|session| &session.authentication)
3067 {
3068 Some(SessionAuthentication::TokenChallenge {
3069 algorithm: TokenChallengeAlgorithm::Blake3,
3070 nonce,
3071 server_timestamp,
3072 ..
3073 }) => {
3074 let response = crate::admin::AuthenticationToken::compute_challenge_response_blake3(
3075 token,
3076 nonce,
3077 *server_timestamp,
3078 );
3079 challenge_session.authenticate(Authentication::TokenChallengeResponse(Bytes::from(
3080 response.as_bytes().to_vec(),
3081 )))
3082 }
3083 _ => Err(crate::Error::InvalidCredentials),
3084 }
3085 }
3086
3087 /// Authenticates a [`User`](crate::admin::User) using a password. If
3088 /// successful, the returned instance will have the permissions from
3089 /// `identity`.
3090 #[cfg(feature = "password-hashing")]
3091 fn authenticate_with_password<'name, User: Nameable<'name, u64>>(
3092 &self,
3093 user: User,
3094 password: SensitiveString,
3095 ) -> Result<Self::Authenticated, crate::Error> {
3096 self.authenticate(Authentication::password(user, password)?)
3097 }
3098
3099 /// Adds a user to a permission group.
3100 fn add_permission_group_to_user<
3101 'user,
3102 'group,
3103 U: Nameable<'user, u64> + Send + Sync,
3104 G: Nameable<'group, u64> + Send + Sync,
3105 >(
3106 &self,
3107 user: U,
3108 permission_group: G,
3109 ) -> Result<(), crate::Error>;
3110
3111 /// Removes a user from a permission group.
3112 fn remove_permission_group_from_user<
3113 'user,
3114 'group,
3115 U: Nameable<'user, u64> + Send + Sync,
3116 G: Nameable<'group, u64> + Send + Sync,
3117 >(
3118 &self,
3119 user: U,
3120 permission_group: G,
3121 ) -> Result<(), crate::Error>;
3122
3123 /// Adds a user to a permission group.
3124 fn add_role_to_user<
3125 'user,
3126 'role,
3127 U: Nameable<'user, u64> + Send + Sync,
3128 R: Nameable<'role, u64> + Send + Sync,
3129 >(
3130 &self,
3131 user: U,
3132 role: R,
3133 ) -> Result<(), crate::Error>;
3134
3135 /// Removes a user from a permission group.
3136 fn remove_role_from_user<
3137 'user,
3138 'role,
3139 U: Nameable<'user, u64> + Send + Sync,
3140 R: Nameable<'role, u64> + Send + Sync,
3141 >(
3142 &self,
3143 user: U,
3144 role: R,
3145 ) -> Result<(), crate::Error>;
3146}
3147
3148/// Functions for interacting with a multi-database BonsaiDb instance.
3149#[async_trait]
3150pub trait AsyncStorageConnection: HasSession + Sized + Send + Sync {
3151 /// The type that represents a database for this implementation.
3152 type Database: AsyncConnection;
3153 /// The [`StorageConnection`] type returned from authentication calls.
3154 type Authenticated: AsyncStorageConnection;
3155
3156 /// Returns the currently authenticated session, if any.
3157 async fn admin(&self) -> Self::Database;
3158 /// Creates a database named `name` with the `Schema` provided.
3159 ///
3160 /// ## Errors
3161 ///
3162 /// * [`Error::InvalidDatabaseName`]: `name` must begin with an alphanumeric
3163 /// character (`[a-zA-Z0-9]`), and all remaining characters must be
3164 /// alphanumeric, a period (`.`), or a hyphen (`-`).
3165 /// * [`Error::DatabaseNameAlreadyTaken`]: `name` was already used for a
3166 /// previous database name. Returned if `only_if_needed` is false.
3167 async fn create_database<DB: Schema>(
3168 &self,
3169 name: &str,
3170 only_if_needed: bool,
3171 ) -> Result<Self::Database, crate::Error> {
3172 self.create_database_with_schema(name, DB::schema_name(), only_if_needed)
3173 .await?;
3174 self.database::<DB>(name).await
3175 }
3176
3177 /// Returns a reference to database `name` with schema `DB`.
3178 async fn database<DB: Schema>(&self, name: &str) -> Result<Self::Database, crate::Error>;
3179
3180 /// Creates a database named `name` using the [`SchemaName`] `schema`.
3181 ///
3182 /// ## Errors
3183 ///
3184 /// * [`Error::InvalidDatabaseName`]: `name` must begin with an alphanumeric
3185 /// character (`[a-zA-Z0-9]`), and all remaining characters must be
3186 /// alphanumeric, a period (`.`), or a hyphen (`-`).
3187 /// * [`Error::DatabaseNameAlreadyTaken`]: `name` was already used for a
3188 /// previous database name. Returned if `only_if_needed` is false.
3189 async fn create_database_with_schema(
3190 &self,
3191 name: &str,
3192 schema: SchemaName,
3193 only_if_needed: bool,
3194 ) -> Result<(), crate::Error>;
3195
3196 /// Deletes a database named `name`.
3197 ///
3198 /// ## Errors
3199 ///
3200 /// * [`Error::DatabaseNotFound`]: database `name` does not exist.
3201 /// * [`Error::Other`]: an error occurred while deleting files.
3202 async fn delete_database(&self, name: &str) -> Result<(), crate::Error>;
3203
3204 /// Lists the databases in this storage.
3205 async fn list_databases(&self) -> Result<Vec<Database>, crate::Error>;
3206
3207 /// Lists the [`SchemaName`]s registered with this storage.
3208 async fn list_available_schemas(&self) -> Result<Vec<SchemaSummary>, crate::Error>;
3209
3210 /// Creates a user.
3211 async fn create_user(&self, username: &str) -> Result<u64, crate::Error>;
3212
3213 /// Deletes a user.
3214 async fn delete_user<'user, U: Nameable<'user, u64> + Send + Sync>(
3215 &self,
3216 user: U,
3217 ) -> Result<(), crate::Error>;
3218
3219 /// Sets a user's password.
3220 #[cfg(feature = "password-hashing")]
3221 async fn set_user_password<'user, U: Nameable<'user, u64> + Send + Sync>(
3222 &self,
3223 user: U,
3224 password: SensitiveString,
3225 ) -> Result<(), crate::Error>;
3226
3227 /// Authenticates using an
3228 /// [`AuthenticationToken`](crate::admin::AuthenticationToken). If
3229 /// successful, the returned instance will have the permissions from
3230 /// `identity`.
3231 #[cfg(any(feature = "token-authentication", feature = "password-hashing"))]
3232 async fn authenticate(
3233 &self,
3234 authentication: Authentication,
3235 ) -> Result<Self::Authenticated, crate::Error>;
3236
3237 /// Authenticates using an
3238 /// [`AuthenticationToken`](crate::admin::AuthenticationToken). If
3239 /// successful, the returned instance will have the permissions from
3240 /// `identity`.
3241 #[cfg(feature = "token-authentication")]
3242 async fn authenticate_with_token(
3243 &self,
3244 id: u64,
3245 token: &SensitiveString,
3246 ) -> Result<<Self::Authenticated as AsyncStorageConnection>::Authenticated, crate::Error> {
3247 let challenge_session = self.authenticate(Authentication::token(id, token)?).await?;
3248 match challenge_session
3249 .session()
3250 .map(|session| &session.authentication)
3251 {
3252 Some(SessionAuthentication::TokenChallenge {
3253 algorithm: TokenChallengeAlgorithm::Blake3,
3254 nonce,
3255 server_timestamp,
3256 ..
3257 }) => {
3258 let response = crate::admin::AuthenticationToken::compute_challenge_response_blake3(
3259 token,
3260 nonce,
3261 *server_timestamp,
3262 );
3263 challenge_session
3264 .authenticate(Authentication::TokenChallengeResponse(Bytes::from(
3265 response.as_bytes().to_vec(),
3266 )))
3267 .await
3268 }
3269 _ => Err(crate::Error::InvalidCredentials),
3270 }
3271 }
3272
3273 /// Authenticates a [`User`](crate::admin::User) using a password. If
3274 /// successful, the returned instance will have the permissions from
3275 /// `identity`.
3276 #[cfg(feature = "password-hashing")]
3277 async fn authenticate_with_password<'name, User: Nameable<'name, u64> + Send>(
3278 &self,
3279 user: User,
3280 password: SensitiveString,
3281 ) -> Result<Self::Authenticated, crate::Error> {
3282 self.authenticate(Authentication::password(user, password)?)
3283 .await
3284 }
3285
3286 /// Assumes the `identity`. If successful, the returned instance will have
3287 /// the merged permissions of the current authentication session and the
3288 /// permissions from `identity`.
3289 async fn assume_identity(
3290 &self,
3291 identity: IdentityReference<'_>,
3292 ) -> Result<Self::Authenticated, crate::Error>;
3293
3294 /// Adds a user to a permission group.
3295 async fn add_permission_group_to_user<
3296 'user,
3297 'group,
3298 U: Nameable<'user, u64> + Send + Sync,
3299 G: Nameable<'group, u64> + Send + Sync,
3300 >(
3301 &self,
3302 user: U,
3303 permission_group: G,
3304 ) -> Result<(), crate::Error>;
3305
3306 /// Removes a user from a permission group.
3307 async fn remove_permission_group_from_user<
3308 'user,
3309 'group,
3310 U: Nameable<'user, u64> + Send + Sync,
3311 G: Nameable<'group, u64> + Send + Sync,
3312 >(
3313 &self,
3314 user: U,
3315 permission_group: G,
3316 ) -> Result<(), crate::Error>;
3317
3318 /// Adds a user to a permission group.
3319 async fn add_role_to_user<
3320 'user,
3321 'role,
3322 U: Nameable<'user, u64> + Send + Sync,
3323 R: Nameable<'role, u64> + Send + Sync,
3324 >(
3325 &self,
3326 user: U,
3327 role: R,
3328 ) -> Result<(), crate::Error>;
3329
3330 /// Removes a user from a permission group.
3331 async fn remove_role_from_user<
3332 'user,
3333 'role,
3334 U: Nameable<'user, u64> + Send + Sync,
3335 R: Nameable<'role, u64> + Send + Sync,
3336 >(
3337 &self,
3338 user: U,
3339 role: R,
3340 ) -> Result<(), crate::Error>;
3341}
3342
3343/// A database stored in BonsaiDb.
3344#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
3345pub struct Database {
3346 /// The name of the database.
3347 pub name: String,
3348 /// The schema defining the database.
3349 pub schema: SchemaName,
3350}
3351
3352/// A string containing sensitive (private) data. This struct automatically
3353/// overwrites its contents with zeroes when dropped.
3354#[derive(Clone, Default, Serialize, Deserialize, Zeroize, Eq, PartialEq)]
3355#[zeroize(drop)]
3356#[serde(transparent)]
3357pub struct SensitiveString(pub String);
3358
3359impl std::fmt::Debug for SensitiveString {
3360 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3361 f.write_str("SensitiveString(...)")
3362 }
3363}
3364
3365impl Deref for SensitiveString {
3366 type Target = String;
3367
3368 fn deref(&self) -> &Self::Target {
3369 &self.0
3370 }
3371}
3372
3373impl DerefMut for SensitiveString {
3374 fn deref_mut(&mut self) -> &mut Self::Target {
3375 &mut self.0
3376 }
3377}
3378
3379impl<'k> Key<'k> for SensitiveString {
3380 const CAN_OWN_BYTES: bool = String::CAN_OWN_BYTES;
3381
3382 fn from_ord_bytes<'e>(bytes: ByteSource<'k, 'e>) -> Result<Self, Self::Error> {
3383 String::from_ord_bytes(bytes).map(Self)
3384 }
3385}
3386
3387impl KeyEncoding<Self> for SensitiveString {
3388 type Error = FromUtf8Error;
3389
3390 const LENGTH: Option<usize> = None;
3391
3392 fn describe<Visitor>(visitor: &mut Visitor)
3393 where
3394 Visitor: KeyVisitor,
3395 {
3396 visitor.visit_type(KeyKind::String);
3397 }
3398
3399 fn as_ord_bytes(&self) -> Result<std::borrow::Cow<'_, [u8]>, Self::Error> {
3400 self.0.as_ord_bytes()
3401 }
3402}
3403
3404impl From<String> for SensitiveString {
3405 fn from(sensitive: String) -> Self {
3406 Self(sensitive)
3407 }
3408}
3409
3410impl<'a> From<&'a str> for SensitiveString {
3411 fn from(sensitive: &'a str) -> Self {
3412 Self(sensitive.to_owned())
3413 }
3414}
3415
3416/// A buffer containing sensitive (private) data. This struct automatically
3417/// overwrites its contents with zeroes when dropped.
3418#[derive(Clone, Serialize, Deserialize, Zeroize, Eq, PartialEq)]
3419#[zeroize(drop)]
3420#[serde(transparent)]
3421pub struct SensitiveBytes(pub Bytes);
3422
3423impl std::fmt::Debug for SensitiveBytes {
3424 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3425 f.write_str("SensitiveBytes(...)")
3426 }
3427}
3428
3429impl Deref for SensitiveBytes {
3430 type Target = Bytes;
3431
3432 fn deref(&self) -> &Self::Target {
3433 &self.0
3434 }
3435}
3436
3437impl DerefMut for SensitiveBytes {
3438 fn deref_mut(&mut self) -> &mut Self::Target {
3439 &mut self.0
3440 }
3441}
3442
3443impl<'k> Key<'k> for SensitiveBytes {
3444 const CAN_OWN_BYTES: bool = Bytes::CAN_OWN_BYTES;
3445
3446 fn from_ord_bytes<'e>(bytes: ByteSource<'k, 'e>) -> Result<Self, Self::Error> {
3447 Bytes::from_ord_bytes(bytes).map(Self)
3448 }
3449}
3450
3451impl KeyEncoding<Self> for SensitiveBytes {
3452 type Error = Infallible;
3453
3454 const LENGTH: Option<usize> = None;
3455
3456 fn describe<Visitor>(visitor: &mut Visitor)
3457 where
3458 Visitor: KeyVisitor,
3459 {
3460 visitor.visit_type(KeyKind::Bytes);
3461 }
3462
3463 fn as_ord_bytes(&self) -> Result<std::borrow::Cow<'_, [u8]>, Self::Error> {
3464 self.0.as_ord_bytes()
3465 }
3466}
3467
3468/// Authentication methods.
3469#[derive(Clone, Debug, Serialize, Deserialize)]
3470#[must_use]
3471pub enum Authentication {
3472 /// Initialize token-based authentication.
3473 #[cfg(feature = "token-authentication")]
3474 Token {
3475 /// The unique token id.
3476 id: u64,
3477 /// The current timestamp of the authenticating device. This must be
3478 /// within 5 minutes of the server's time for token authentication to
3479 /// succeed.
3480 now: crate::key::time::TimestampAsNanoseconds,
3481 /// The hash of `now`, using the private token as key matter.
3482 now_hash: Bytes,
3483 /// The token challenge algorithm used to generate `now_hash`.
3484 algorithm: TokenChallengeAlgorithm,
3485 },
3486 /// A response to the server's token authentication challenge.
3487 #[cfg(feature = "token-authentication")]
3488 TokenChallengeResponse(Bytes),
3489 /// Authenticate a user with a password.
3490 #[cfg(feature = "password-hashing")]
3491 Password {
3492 /// The username or the user id to authenticate as.
3493 user: NamedReference<'static, u64>,
3494 /// The password of the user.
3495 password: SensitiveString,
3496 },
3497}
3498
3499impl Authentication {
3500 /// Returns an authentication instance for this user and password.
3501 #[cfg(feature = "password-hashing")]
3502 pub fn password<'user, UsernameOrId: Nameable<'user, u64>>(
3503 user: UsernameOrId,
3504 password: SensitiveString,
3505 ) -> Result<Self, crate::Error> {
3506 Ok(Self::Password {
3507 user: user.name()?.into_owned(),
3508 password,
3509 })
3510 }
3511
3512 /// Returns a token authentication initialization instance for this token.
3513 #[cfg(feature = "token-authentication")]
3514 pub fn token(id: u64, token: &SensitiveString) -> Result<Self, crate::Error> {
3515 let now = crate::key::time::TimestampAsNanoseconds::now();
3516 Ok(Self::Token {
3517 id,
3518 now,
3519 now_hash: Bytes::from(
3520 crate::admin::AuthenticationToken::compute_request_time_hash_blake3(now, token)
3521 .as_bytes()
3522 .to_vec(),
3523 ),
3524 algorithm: TokenChallengeAlgorithm::Blake3,
3525 })
3526 }
3527}
3528
3529#[doc(hidden)]
3530#[macro_export]
3531macro_rules! __doctest_prelude {
3532 () => {
3533 use bonsaidb_core::{
3534 connection::AccessPolicy,
3535 define_basic_unique_mapped_view,
3536 document::{CollectionDocument,Emit, Document, OwnedDocument},
3537 schema::{
3538 Collection, CollectionName, DefaultSerialization,
3539 DefaultViewSerialization, Name, NamedCollection, ReduceResult, Schema, SchemaName,
3540 Schematic, SerializedCollection, View, ViewSchema, CollectionMapReduce, ViewMapResult, ViewMappedValue, SerializedView,
3541 },
3542 Error,
3543 };
3544 use serde::{Deserialize, Serialize};
3545
3546 #[derive(Debug, Schema)]
3547 #[schema(name = "MySchema", collections = [MyCollection], core = $crate)]
3548 pub struct MySchema;
3549
3550 #[derive( Debug, Serialize, Deserialize, Default, Collection)]
3551 #[collection(name = "MyCollection", views = [MyCollectionByName], core = $crate)]
3552 pub struct MyCollection {
3553 pub name: String,
3554 pub rank: u32,
3555 pub score: f32,
3556 }
3557
3558 impl MyCollection {
3559 pub fn named(s: impl Into<String>) -> Self {
3560 Self::new(s, 0, 0.)
3561 }
3562
3563 pub fn new(s: impl Into<String>, rank: u32, score: f32) -> Self {
3564 Self {
3565 name: s.into(),
3566 rank,
3567 score,
3568 }
3569 }
3570 }
3571
3572 impl NamedCollection for MyCollection {
3573 type ByNameView = MyCollectionByName;
3574 }
3575
3576 #[derive(Debug, Clone, View, ViewSchema)]
3577 #[view(collection = MyCollection, key = u32, value = f32, name = "scores-by-rank", core = $crate)]
3578 #[view_schema(core = $crate)]
3579 pub struct ScoresByRank;
3580
3581 impl CollectionMapReduce for ScoresByRank {
3582 fn map<'doc>(
3583 &self,
3584 document: CollectionDocument<<Self::View as View>::Collection>,
3585 ) -> ViewMapResult<'doc, Self::View> {
3586 document
3587 .header
3588 .emit_key_and_value(document.contents.rank, document.contents.score)
3589 }
3590
3591 fn reduce(
3592 &self,
3593 mappings: &[ViewMappedValue<'_, Self::View>],
3594 rereduce: bool,
3595 ) -> ReduceResult<Self::View> {
3596 if mappings.is_empty() {
3597 Ok(0.)
3598 } else {
3599 Ok(mappings.iter().map(|map| map.value).sum::<f32>() / mappings.len() as f32)
3600 }
3601 }
3602 }
3603
3604 define_basic_unique_mapped_view!(
3605 MyCollectionByName,
3606 MyCollection,
3607 1,
3608 "by-name",
3609 String,
3610 (),
3611 |document: CollectionDocument<MyCollection>| {
3612 document.header.emit_key(document.contents.name.clone())
3613 },
3614 );
3615 };
3616}
3617
3618/// The authentication state for a [`StorageConnection`].
3619#[derive(Default, Clone, Debug, Serialize, Deserialize)]
3620#[must_use]
3621pub struct Session {
3622 /// The session's unique ID.
3623 pub id: Option<SessionId>,
3624 /// The authenticated identity, if any.
3625 pub authentication: SessionAuthentication,
3626 /// The effective permissions of the session.
3627 pub permissions: Permissions,
3628}
3629
3630/// The authentication state of a [`Session`].
3631#[derive(Hash, Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
3632pub enum SessionAuthentication {
3633 /// The session is unauthenticated.
3634 None,
3635 /// The session is authenticated as an identity.
3636 Identity(Arc<Identity>),
3637 /// The session is pending authentication using a token.
3638 #[cfg(feature = "token-authentication")]
3639 TokenChallenge {
3640 /// The id of the token being authenticated
3641 id: u64,
3642 /// The algorithm the server has chosen for the token challenge.
3643 algorithm: TokenChallengeAlgorithm,
3644 /// Random data generated by the server to be hashed during the
3645 /// challenge.
3646 nonce: [u8; 32],
3647 /// The server timestamp that is used for authenticated extra data.
3648 server_timestamp: crate::key::time::TimestampAsNanoseconds,
3649 },
3650}
3651
3652impl Default for SessionAuthentication {
3653 fn default() -> Self {
3654 Self::None
3655 }
3656}
3657
3658/// A token challenge algorith designates with which algorthm to authenticate
3659/// tokens.
3660#[derive(Hash, Eq, PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
3661#[non_exhaustive]
3662#[cfg(feature = "token-authentication")]
3663pub enum TokenChallengeAlgorithm {
3664 /// Authenticate tokens using [`blake3`](https://crates.io/crates/blake3).
3665 ///
3666 /// The initial request requires a hash of
3667 /// [`TimestampAsNanoseconds::now()`](crate::key::time::TimestampAsNanoseconds::now)
3668 /// to be performed using [`blake3::keyed_hash()`]. The key is derived using
3669 /// [`blake3::derive_key()`] using a context formatted like this: `bonsaidb
3670 /// {now} token-authentication`. The `now` value should be timestamp's
3671 /// nanoseconds relative to
3672 /// [`BonsaiEpoch`](crate::key::time::limited::BonsaiEpoch), and the hash's
3673 /// contents should be the 8-byte big-endian representation of the
3674 /// nanoseconds as an i64.
3675 ///
3676 /// The storage will verify that the timestamp is within a reasonable delta
3677 /// of the server's current time, and it will verify the private token was
3678 /// used to generate the hash sent. To prevent replay attacks and add
3679 /// additional security, the server will return a new [`Session`] whose
3680 /// authentication field is [`SessionAuthentication::TokenChallenge`].
3681 ///
3682 /// The connector must use the new connection to call `authenticate()` with
3683 /// [`Authentication::TokenChallengeResponse`]. It is possible that the
3684 /// server will elect a different challenge algorithm than the connector
3685 /// chose when initially authenticating.
3686 ///
3687 /// To generate the challenge response for [`blake3`],
3688 /// [`blake3::keyed_hash()`] is used to hash the `nonce`. The key is derived
3689 /// using [`blake3::derive_key()`] using a context formatted like this:
3690 /// `bonsaidb {server_timestamp} token-challenge`. The `server_timestamp`
3691 /// value should be timestamp's nanoseconds relative to
3692 /// [`BonsaiEpoch`](crate::key::time::limited::BonsaiEpoch).
3693 Blake3,
3694}
3695
3696/// Methods for authentication.
3697#[derive(Action, Serialize, Deserialize, Clone, Copy, Debug)]
3698pub enum AuthenticationMethod {
3699 /// Authenticate the user or role using an
3700 /// [`AuthenticationToken`](crate::admin::AuthenticationToken).
3701 Token,
3702 /// Authenticate a user using password hashing (Argon2).
3703 PasswordHash,
3704}
3705
3706/// A unique session ID.
3707#[derive(Default, Clone, Copy, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
3708#[serde(transparent)]
3709pub struct SessionId(pub u64);
3710
3711impl Session {
3712 /// Checks if `action` is permitted against `resource_name`.
3713 pub fn allowed_to<'a, R: AsRef<[Identifier<'a>]>, P: Action>(
3714 &self,
3715 resource_name: R,
3716 action: &P,
3717 ) -> bool {
3718 self.permissions.allowed_to(resource_name, action)
3719 }
3720
3721 /// Checks if `action` is permitted against `resource_name`. If permission
3722 /// is denied, returns a [`PermissionDenied`](Error::PermissionDenied)
3723 /// error.
3724 pub fn check_permission<'a, R: AsRef<[Identifier<'a>]>, P: Action>(
3725 &self,
3726 resource_name: R,
3727 action: &P,
3728 ) -> Result<(), Error> {
3729 self.permissions
3730 .check(resource_name, action)
3731 .map_err(Error::from)
3732 }
3733
3734 /// Returns the identity that this session is authenticated as, if any.
3735 #[must_use]
3736 pub fn identity(&self) -> Option<&Identity> {
3737 if let SessionAuthentication::Identity(identity) = &self.authentication {
3738 Some(identity)
3739 } else {
3740 None
3741 }
3742 }
3743}
3744
3745impl Eq for Session {}
3746
3747impl PartialEq for Session {
3748 fn eq(&self, other: &Self) -> bool {
3749 self.authentication == other.authentication
3750 }
3751}
3752
3753impl std::hash::Hash for Session {
3754 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
3755 self.authentication.hash(state);
3756 }
3757}
3758
3759/// An identity from the connected BonsaiDb instance.
3760#[derive(Clone, Debug, Serialize, Deserialize)]
3761#[non_exhaustive]
3762pub enum Identity {
3763 /// A [`User`](crate::admin::User).
3764 User {
3765 /// The unique ID of the user.
3766 id: u64,
3767 /// The username of the user.
3768 username: String,
3769 },
3770 /// A [`Role`](crate::admin::Role).
3771 Role {
3772 /// The unique ID of the role.
3773 id: u64,
3774 /// The name of the role.
3775 name: String,
3776 },
3777}
3778
3779impl Eq for Identity {}
3780
3781impl PartialEq for Identity {
3782 fn eq(&self, other: &Self) -> bool {
3783 match (self, other) {
3784 (Self::User { id: l_id, .. }, Self::User { id: r_id, .. })
3785 | (Self::Role { id: l_id, .. }, Self::Role { id: r_id, .. }) => l_id == r_id,
3786 _ => false,
3787 }
3788 }
3789}
3790
3791impl std::hash::Hash for Identity {
3792 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
3793 match self {
3794 Identity::User { id, .. } => {
3795 0_u8.hash(state); // "Tag" for the variant
3796 id.hash(state);
3797 }
3798 Identity::Role { id, .. } => {
3799 1_u8.hash(state); // "Tag" for the variant
3800 id.hash(state);
3801 }
3802 }
3803 }
3804}
3805
3806/// A reference to an identity.
3807#[derive(Clone, Debug, Serialize, Deserialize)]
3808#[non_exhaustive]
3809pub enum IdentityReference<'name> {
3810 /// A reference to a [`User`](crate::admin::User).
3811 User(NamedReference<'name, u64>),
3812 /// A reference to a [`Role`](crate::admin::Role).
3813 Role(NamedReference<'name, u64>),
3814}
3815
3816impl<'name> IdentityReference<'name> {
3817 /// Returns a reference to a [`User`]. This function accepts either the
3818 /// user's unique id or their username.
3819 pub fn user<User: Nameable<'name, u64>>(user: User) -> Result<Self, crate::Error> {
3820 Ok(Self::User(user.name()?))
3821 }
3822
3823 /// Returns a reference to a [`Role`]. This function accepts either the
3824 /// role's unique id or the role's name.
3825 pub fn role<Role: Nameable<'name, u64>>(role: Role) -> Result<Self, crate::Error> {
3826 Ok(Self::Role(role.name()?))
3827 }
3828
3829 /// Converts this reference to an owned reference with a `'static` lifetime.
3830 #[must_use]
3831 pub fn into_owned(self) -> IdentityReference<'static> {
3832 match self {
3833 IdentityReference::User(user) => IdentityReference::User(user.into_owned()),
3834 IdentityReference::Role(role) => IdentityReference::Role(role.into_owned()),
3835 }
3836 }
3837
3838 /// Resolves this reference to the unique id.
3839 pub fn resolve<C: Connection>(&self, admin: &C) -> Result<Option<IdentityId>, crate::Error> {
3840 match self {
3841 IdentityReference::User(name) => Ok(name.id::<User, _>(admin)?.map(IdentityId::User)),
3842 IdentityReference::Role(name) => Ok(name.id::<Role, _>(admin)?.map(IdentityId::Role)),
3843 }
3844 }
3845
3846 /// Resolves this reference to the unique id.
3847 pub async fn resolve_async<C: AsyncConnection>(
3848 &self,
3849 admin: &C,
3850 ) -> Result<Option<IdentityId>, crate::Error> {
3851 match self {
3852 IdentityReference::User(name) => {
3853 Ok(name.id_async::<User, _>(admin).await?.map(IdentityId::User))
3854 }
3855 IdentityReference::Role(name) => {
3856 Ok(name.id_async::<Role, _>(admin).await?.map(IdentityId::Role))
3857 }
3858 }
3859 }
3860}
3861
3862/// An identity from the connected BonsaiDb instance.
3863#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
3864#[non_exhaustive]
3865pub enum IdentityId {
3866 /// A [`User`](crate::admin::User) id.
3867 User(u64),
3868 /// A [`Role`](crate::admin::Role) id.
3869 Role(u64),
3870}
3871
3872/// This type is the result of `query()`. It is a list of mappings, which
3873/// contains:
3874///
3875/// - The key emitted during the map function.
3876/// - The value emitted during the map function.
3877/// - The source document header that the mappings originated from.
3878///
3879/// This type alias is being moved to [`schema::view::map::ViewMappings`] and
3880/// will be removed in v0.6.0.
3881#[deprecated(
3882 since = "0.5.0",
3883 note = "ViewMappings has been moved to bonsaidb_core::schema::view::ViewMappings"
3884)]
3885pub type ViewMappings<V> = schema::view::map::ViewMappings<V>;