use std::borrow::Borrow;
use std::convert::Infallible;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::string::FromUtf8Error;
use std::sync::Arc;
use actionable::{Action, Identifier};
use arc_bytes::serde::Bytes;
use async_trait::async_trait;
use futures::future::BoxFuture;
use futures::{Future, FutureExt};
use serde::{Deserialize, Serialize};
use zeroize::Zeroize;
use crate::admin::{Role, User};
use crate::document::{
CollectionDocument, CollectionHeader, Document, HasHeader, Header, OwnedDocument,
};
use crate::key::{ByteSource, IntoPrefixRange, Key, KeyEncoding, KeyKind, KeyVisitor};
use crate::permissions::Permissions;
use crate::schema::view::map::{MappedDocuments, ViewMappings as ViewMappingsCurrent};
use crate::schema::{
self, MappedValue, Nameable, NamedReference, Schema, SchemaName, SchemaSummary,
SerializedCollection,
};
use crate::{transaction, Error};
mod has_session;
mod lowlevel;
pub use self::has_session::HasSession;
pub use self::lowlevel::{AsyncLowLevelConnection, HasSchema, LowLevelConnection};
pub trait Connection: LowLevelConnection + Sized + Send + Sync {
type Storage: StorageConnection<Database = Self>;
fn storage(&self) -> Self::Storage;
fn collection<C: schema::Collection>(&self) -> Collection<'_, Self, C> {
Collection::new(self)
}
fn view<V: schema::SerializedView>(&'_ self) -> View<'_, Self, V, V::Key> {
View::new(self)
}
fn list_executed_transactions(
&self,
starting_id: Option<u64>,
result_limit: Option<u32>,
) -> Result<Vec<transaction::Executed>, Error>;
fn last_transaction_id(&self) -> Result<Option<u64>, Error>;
fn compact(&self) -> Result<(), crate::Error>;
fn compact_collection<C: schema::Collection>(&self) -> Result<(), crate::Error> {
self.compact_collection_by_name(C::collection_name())
}
fn compact_key_value_store(&self) -> Result<(), crate::Error>;
}
pub struct Collection<'a, Cn, Cl> {
connection: &'a Cn,
_phantom: PhantomData<Cl>,
}
impl<'a, Cn, Cl> Clone for Collection<'a, Cn, Cl> {
fn clone(&self) -> Self {
Self {
connection: self.connection,
_phantom: PhantomData,
}
}
}
impl<'a, Cn, Cl> Collection<'a, Cn, Cl>
where
Cn: Connection,
Cl: schema::Collection,
{
fn new(connection: &'a Cn) -> Self {
Self {
connection,
_phantom: PhantomData,
}
}
pub fn push(
&self,
item: &<Cl as SerializedCollection>::Contents,
) -> Result<CollectionHeader<Cl::PrimaryKey>, crate::Error>
where
Cl: schema::SerializedCollection,
{
let contents = Cl::serialize(item)?;
if let Some(natural_id) = Cl::natural_id(item) {
self.insert_bytes(&natural_id, contents)
} else {
self.push_bytes(contents)
}
}
pub fn push_bytes<B: Into<Bytes> + Send>(
&self,
contents: B,
) -> Result<CollectionHeader<Cl::PrimaryKey>, crate::Error> {
self.connection
.insert::<Cl, _, B>(Option::<&Cl::PrimaryKey>::None, contents)
}
pub fn insert<PrimaryKey>(
&self,
id: &PrimaryKey,
item: &<Cl as SerializedCollection>::Contents,
) -> Result<CollectionHeader<Cl::PrimaryKey>, crate::Error>
where
Cl: schema::SerializedCollection,
PrimaryKey: KeyEncoding<Cl::PrimaryKey> + ?Sized,
{
let contents = Cl::serialize(item)?;
self.connection.insert::<Cl, _, _>(Some(id), contents)
}
pub fn insert_bytes<PrimaryKey, B: Into<Bytes> + Send>(
&self,
id: &PrimaryKey,
contents: B,
) -> Result<CollectionHeader<Cl::PrimaryKey>, crate::Error>
where
PrimaryKey: KeyEncoding<Cl::PrimaryKey> + ?Sized,
{
self.connection.insert::<Cl, _, B>(Some(id), contents)
}
pub fn update<D: Document<Cl> + Send + Sync>(&self, doc: &mut D) -> Result<(), Error> {
self.connection.update::<Cl, D>(doc)
}
pub fn overwrite<D: Document<Cl> + Send + Sync>(&self, doc: &mut D) -> Result<(), Error> {
let contents = doc.bytes()?;
doc.set_collection_header(self.connection.overwrite::<Cl, _>(doc.id(), contents)?)
}
pub fn get<PrimaryKey>(&self, id: &PrimaryKey) -> Result<Option<OwnedDocument>, Error>
where
PrimaryKey: KeyEncoding<Cl::PrimaryKey> + ?Sized,
{
self.connection.get::<Cl, _>(id)
}
pub fn get_multiple<'id, DocumentIds, PrimaryKey, I>(
&self,
ids: DocumentIds,
) -> Result<Vec<OwnedDocument>, Error>
where
DocumentIds: IntoIterator<Item = &'id PrimaryKey, IntoIter = I> + Send + Sync,
I: Iterator<Item = &'id PrimaryKey> + Send + Sync,
PrimaryKey: KeyEncoding<Cl::PrimaryKey> + 'id + ?Sized,
{
self.connection.get_multiple::<Cl, _, _, _>(ids)
}
pub fn list<PrimaryKey, R>(&'a self, ids: R) -> List<'a, Cn, Cl, PrimaryKey>
where
R: Into<Range<&'a PrimaryKey>>,
PrimaryKey: KeyEncoding<Cl::PrimaryKey> + PartialEq + 'a + ?Sized,
Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
{
List::new(MaybeOwned::Borrowed(self), RangeRef::borrowed(ids.into()))
}
pub fn list_with_prefix<PrimaryKey>(
&'a self,
prefix: &'a PrimaryKey,
) -> List<'a, Cn, Cl, PrimaryKey>
where
PrimaryKey:
IntoPrefixRange<'a, Cl::PrimaryKey> + KeyEncoding<Cl::PrimaryKey> + PartialEq + ?Sized,
Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
{
List::new(MaybeOwned::Borrowed(self), prefix.to_prefix_range())
}
pub fn all(&'a self) -> List<'a, Cn, Cl, Cl::PrimaryKey> {
List::new(
MaybeOwned::Borrowed(self),
RangeRef::borrowed(Range::from(..)),
)
}
pub fn delete<H: HasHeader + Send + Sync>(&self, doc: &H) -> Result<(), Error> {
self.connection.delete::<Cl, H>(doc)
}
}
#[must_use]
pub struct List<'a, Cn, Cl, PrimaryKey>
where
Cl: schema::Collection,
PrimaryKey: PartialEq + ?Sized,
Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
{
collection: MaybeOwned<'a, Collection<'a, Cn, Cl>>,
range: RangeRef<'a, Cl::PrimaryKey, PrimaryKey>,
sort: Sort,
limit: Option<u32>,
}
impl<'a, Cn, Cl, PrimaryKey> List<'a, Cn, Cl, PrimaryKey>
where
Cl: schema::Collection,
Cn: Connection,
PrimaryKey: KeyEncoding<Cl::PrimaryKey> + PartialEq + 'a + ?Sized,
Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
{
pub(crate) const fn new(
collection: MaybeOwned<'a, Collection<'a, Cn, Cl>>,
range: RangeRef<'a, Cl::PrimaryKey, PrimaryKey>,
) -> Self {
Self {
collection,
range,
sort: Sort::Ascending,
limit: None,
}
}
pub const fn ascending(mut self) -> Self {
self.sort = Sort::Ascending;
self
}
pub const fn descending(mut self) -> Self {
self.sort = Sort::Descending;
self
}
pub const fn limit(mut self, maximum_results: u32) -> Self {
self.limit = Some(maximum_results);
self
}
pub fn count(self) -> Result<u64, Error> {
let Self {
collection, range, ..
} = self;
collection.connection.count::<Cl, _, _>(range)
}
pub fn headers(self) -> Result<Vec<Header>, Error> {
let Self {
collection,
range,
sort,
limit,
..
} = self;
collection
.connection
.list_headers::<Cl, _, PrimaryKey>(range, sort, limit)
}
pub fn query(self) -> Result<Vec<OwnedDocument>, Error> {
let Self {
collection,
range,
sort,
limit,
} = self;
collection.connection.list::<Cl, _, _>(range, sort, limit)
}
}
#[must_use]
pub struct View<'a, Cn, V: schema::SerializedView, Key>
where
V::Key: Borrow<Key> + PartialEq<Key>,
Key: PartialEq + ?Sized,
{
connection: &'a Cn,
pub key: Option<QueryKey<'a, V::Key, Key>>,
pub access_policy: AccessPolicy,
pub sort: Sort,
pub limit: Option<u32>,
_view: PhantomData<V>,
}
impl<'a, Cn, V, Key> View<'a, Cn, V, Key>
where
V::Key: Borrow<Key> + PartialEq<Key>,
V: schema::SerializedView,
Cn: Connection,
Key: KeyEncoding<V::Key> + PartialEq + ?Sized,
{
const fn new(connection: &'a Cn) -> Self {
Self {
connection,
key: None,
access_policy: AccessPolicy::UpdateBefore,
sort: Sort::Ascending,
limit: None,
_view: PhantomData,
}
}
#[allow(clippy::missing_const_for_fn)] pub fn with_key<K>(self, key: &'a K) -> View<'a, Cn, V, K>
where
K: KeyEncoding<V::Key> + PartialEq + ?Sized,
V::Key: Borrow<K> + PartialEq<K>,
{
View {
connection: self.connection,
key: Some(QueryKey::Matches(MaybeOwned::Borrowed(key))),
access_policy: self.access_policy,
sort: self.sort,
limit: self.limit,
_view: PhantomData,
}
}
pub fn with_keys<K, IntoIter: IntoIterator<Item = &'a K>>(
self,
keys: IntoIter,
) -> View<'a, Cn, V, K>
where
V::Key: Borrow<K> + PartialEq<K>,
K: PartialEq + ?Sized,
{
View {
connection: self.connection,
key: Some(QueryKey::Multiple(
keys.into_iter().map(MaybeOwned::Borrowed).collect(),
)),
access_policy: self.access_policy,
sort: self.sort,
limit: self.limit,
_view: PhantomData,
}
}
pub fn with_key_range<K, R>(self, range: R) -> View<'a, Cn, V, K>
where
R: Into<RangeRef<'a, V::Key, K>>,
K: PartialEq,
V::Key: Borrow<K> + PartialEq<K>,
{
View {
connection: self.connection,
key: Some(QueryKey::Range(range.into())),
access_policy: self.access_policy,
sort: self.sort,
limit: self.limit,
_view: PhantomData,
}
}
pub fn with_key_prefix<K>(self, prefix: &'a K) -> View<'a, Cn, V, K>
where
K: KeyEncoding<V::Key> + IntoPrefixRange<'a, V::Key> + PartialEq + ?Sized,
V::Key: Borrow<K> + PartialEq<K>,
{
View {
connection: self.connection,
key: Some(QueryKey::Range(prefix.to_prefix_range())),
access_policy: self.access_policy,
sort: self.sort,
limit: self.limit,
_view: PhantomData,
}
}
pub const fn with_access_policy(mut self, policy: AccessPolicy) -> Self {
self.access_policy = policy;
self
}
pub const fn ascending(mut self) -> Self {
self.sort = Sort::Ascending;
self
}
pub const fn descending(mut self) -> Self {
self.sort = Sort::Descending;
self
}
pub const fn limit(mut self, maximum_results: u32) -> Self {
self.limit = Some(maximum_results);
self
}
pub fn query(self) -> Result<ViewMappingsCurrent<V>, Error> {
self.connection
.query::<V, Key>(self.key, self.sort, self.limit, self.access_policy)
}
pub fn query_with_docs(self) -> Result<MappedDocuments<OwnedDocument, V>, Error> {
self.connection.query_with_docs::<V, Key>(
self.key,
self.sort,
self.limit,
self.access_policy,
)
}
pub fn query_with_collection_docs(
self,
) -> Result<MappedDocuments<CollectionDocument<V::Collection>, V>, Error>
where
V::Collection: SerializedCollection,
<V::Collection as SerializedCollection>::Contents: std::fmt::Debug,
{
self.connection.query_with_collection_docs::<V, Key>(
self.key,
self.sort,
self.limit,
self.access_policy,
)
}
pub fn reduce(self) -> Result<V::Value, Error> {
self.connection
.reduce::<V, Key>(self.key, self.access_policy)
}
pub fn reduce_grouped(self) -> Result<GroupedReductions<V>, Error> {
self.connection
.reduce_grouped::<V, Key>(self.key, self.access_policy)
}
pub fn delete_docs(self) -> Result<u64, Error> {
self.connection
.delete_docs::<V, Key>(self.key, self.access_policy)
}
}
pub type GroupedReductions<V> =
Vec<MappedValue<<V as schema::View>::Key, <V as schema::View>::Value>>;
#[async_trait]
pub trait AsyncConnection: AsyncLowLevelConnection + Sized + Send + Sync {
type Storage: AsyncStorageConnection<Database = Self>;
fn storage(&self) -> Self::Storage;
fn collection<C: schema::Collection>(&self) -> AsyncCollection<'_, Self, C> {
AsyncCollection::new(self)
}
fn view<V: schema::SerializedView>(&'_ self) -> AsyncView<'_, Self, V, V::Key> {
AsyncView::new(self)
}
async fn list_executed_transactions(
&self,
starting_id: Option<u64>,
result_limit: Option<u32>,
) -> Result<Vec<transaction::Executed>, Error>;
async fn last_transaction_id(&self) -> Result<Option<u64>, Error>;
async fn compact(&self) -> Result<(), crate::Error>;
async fn compact_collection<C: schema::Collection>(&self) -> Result<(), crate::Error> {
self.compact_collection_by_name(C::collection_name()).await
}
async fn compact_key_value_store(&self) -> Result<(), crate::Error>;
}
pub struct AsyncCollection<'a, Cn, Cl> {
connection: &'a Cn,
_phantom: PhantomData<Cl>,
}
impl<'a, Cn, Cl> Clone for AsyncCollection<'a, Cn, Cl> {
fn clone(&self) -> Self {
Self {
connection: self.connection,
_phantom: PhantomData,
}
}
}
impl<'a, Cn, Cl> AsyncCollection<'a, Cn, Cl>
where
Cn: AsyncConnection,
Cl: schema::Collection,
{
fn new(connection: &'a Cn) -> Self {
Self {
connection,
_phantom: PhantomData,
}
}
pub async fn push(
&self,
item: &<Cl as SerializedCollection>::Contents,
) -> Result<CollectionHeader<Cl::PrimaryKey>, crate::Error>
where
Cl: schema::SerializedCollection,
{
let contents = Cl::serialize(item)?;
if let Some(natural_id) = Cl::natural_id(item) {
self.insert_bytes(&natural_id, contents).await
} else {
self.push_bytes(contents).await
}
}
pub async fn push_bytes<B: Into<Bytes> + Send>(
&self,
contents: B,
) -> Result<CollectionHeader<Cl::PrimaryKey>, crate::Error> {
self.connection
.insert::<Cl, _, B>(Option::<&Cl::PrimaryKey>::None, contents)
.await
}
pub async fn insert<PrimaryKey>(
&self,
id: &PrimaryKey,
item: &<Cl as SerializedCollection>::Contents,
) -> Result<CollectionHeader<Cl::PrimaryKey>, crate::Error>
where
Cl: schema::SerializedCollection,
PrimaryKey: KeyEncoding<Cl::PrimaryKey> + ?Sized,
{
let contents = Cl::serialize(item)?;
self.connection.insert::<Cl, _, _>(Some(id), contents).await
}
pub async fn insert_bytes<PrimaryKey, B: Into<Bytes> + Send>(
&self,
id: &PrimaryKey,
contents: B,
) -> Result<CollectionHeader<Cl::PrimaryKey>, crate::Error>
where
PrimaryKey: KeyEncoding<Cl::PrimaryKey> + ?Sized,
{
self.connection.insert::<Cl, _, B>(Some(id), contents).await
}
pub async fn update<D: Document<Cl> + Send + Sync>(&self, doc: &mut D) -> Result<(), Error> {
self.connection.update::<Cl, D>(doc).await
}
pub async fn overwrite<D: Document<Cl> + Send + Sync>(&self, doc: &mut D) -> Result<(), Error> {
let contents = doc.bytes()?;
doc.set_collection_header(
self.connection
.overwrite::<Cl, _>(doc.id(), contents)
.await?,
)
}
pub async fn get<PrimaryKey>(&self, id: &PrimaryKey) -> Result<Option<OwnedDocument>, Error>
where
PrimaryKey: KeyEncoding<Cl::PrimaryKey> + ?Sized,
{
self.connection.get::<Cl, _>(id).await
}
pub async fn get_multiple<'id, DocumentIds, PrimaryKey, I>(
&self,
ids: DocumentIds,
) -> Result<Vec<OwnedDocument>, Error>
where
DocumentIds: IntoIterator<Item = &'id PrimaryKey, IntoIter = I> + Send + Sync,
I: Iterator<Item = &'id PrimaryKey> + Send + Sync,
PrimaryKey: KeyEncoding<Cl::PrimaryKey> + 'id + ?Sized,
{
self.connection.get_multiple::<Cl, _, _, _>(ids).await
}
pub fn list<PrimaryKey, R>(&'a self, ids: R) -> AsyncList<'a, Cn, Cl, PrimaryKey>
where
R: Into<RangeRef<'a, Cl::PrimaryKey, PrimaryKey>>,
PrimaryKey: KeyEncoding<Cl::PrimaryKey> + PartialEq + ?Sized,
Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
{
AsyncList::new(MaybeOwned::Borrowed(self), ids.into())
}
pub fn list_with_prefix<PrimaryKey>(
&'a self,
prefix: &'a PrimaryKey,
) -> AsyncList<'a, Cn, Cl, PrimaryKey>
where
PrimaryKey:
IntoPrefixRange<'a, Cl::PrimaryKey> + KeyEncoding<Cl::PrimaryKey> + PartialEq + ?Sized,
Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
{
AsyncList::new(MaybeOwned::Borrowed(self), prefix.to_prefix_range())
}
pub fn all(&'a self) -> AsyncList<'a, Cn, Cl, Cl::PrimaryKey> {
AsyncList::new(MaybeOwned::Borrowed(self), RangeRef::from(..))
}
pub async fn delete<H: HasHeader + Send + Sync>(&self, doc: &H) -> Result<(), Error> {
self.connection.delete::<Cl, H>(doc).await
}
}
pub(crate) struct AsyncListBuilder<'a, Cn, Cl, PrimaryKey>
where
Cl: schema::Collection,
PrimaryKey: KeyEncoding<Cl::PrimaryKey> + PartialEq + ?Sized,
Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
{
collection: MaybeOwned<'a, AsyncCollection<'a, Cn, Cl>>,
range: RangeRef<'a, Cl::PrimaryKey, PrimaryKey>,
sort: Sort,
limit: Option<u32>,
}
#[derive(Debug)]
pub enum MaybeOwned<'a, TOwned, TBorrowed: ?Sized = TOwned> {
Owned(TOwned),
Borrowed(&'a TBorrowed),
}
impl<'a, TOwned, TBorrowed> Clone for MaybeOwned<'a, TOwned, TBorrowed>
where
TOwned: Clone,
TBorrowed: ?Sized,
{
fn clone(&self) -> Self {
match self {
Self::Owned(value) => Self::Owned(value.clone()),
Self::Borrowed(value) => Self::Borrowed(value),
}
}
}
impl<'a, TOwned, TBorrowed> Deref for MaybeOwned<'a, TOwned, TBorrowed>
where
TOwned: Borrow<TBorrowed>,
TBorrowed: ?Sized,
{
type Target = TBorrowed;
fn deref(&self) -> &TBorrowed {
self.borrow()
}
}
impl<'a, TOwned, TBorrowed> Borrow<TBorrowed> for MaybeOwned<'a, TOwned, TBorrowed>
where
TOwned: Borrow<TBorrowed>,
TBorrowed: ?Sized,
{
fn borrow(&self) -> &TBorrowed {
match self {
MaybeOwned::Owned(value) => value.borrow(),
MaybeOwned::Borrowed(value) => value,
}
}
}
impl<'a, TOwned, TBorrowed> PartialEq for MaybeOwned<'a, TOwned, TBorrowed>
where
TOwned: Borrow<TBorrowed>,
TBorrowed: PartialEq + ?Sized,
{
fn eq(&self, other: &Self) -> bool {
<Self as Borrow<TBorrowed>>::borrow(self).eq(other.borrow())
}
}
impl<'a, TOwned, TBorrowed> PartialOrd for MaybeOwned<'a, TOwned, TBorrowed>
where
TOwned: Borrow<TBorrowed>,
TBorrowed: PartialOrd + ?Sized,
{
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
<Self as Borrow<TBorrowed>>::borrow(self).partial_cmp(other.borrow())
}
}
impl<'a, TOwned, TBorrowed> PartialEq<TBorrowed> for MaybeOwned<'a, TOwned, TBorrowed>
where
TOwned: Borrow<TBorrowed>,
TBorrowed: PartialEq + ?Sized,
{
fn eq(&self, other: &TBorrowed) -> bool {
<Self as Borrow<TBorrowed>>::borrow(self).eq(other)
}
}
impl<'a, TOwned, TBorrowed> PartialOrd<TBorrowed> for MaybeOwned<'a, TOwned, TBorrowed>
where
TOwned: Borrow<TBorrowed>,
TBorrowed: PartialOrd + ?Sized,
{
fn partial_cmp(&self, other: &TBorrowed) -> Option<std::cmp::Ordering> {
<Self as Borrow<TBorrowed>>::borrow(self).partial_cmp(other)
}
}
pub(crate) enum ListState<'a, Cn, Cl, PrimaryKey>
where
Cl: schema::Collection,
PrimaryKey: KeyEncoding<Cl::PrimaryKey> + PartialEq + ?Sized,
Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
{
Pending(Option<AsyncListBuilder<'a, Cn, Cl, PrimaryKey>>),
Executing(BoxFuture<'a, Result<Vec<OwnedDocument>, Error>>),
}
#[must_use]
pub struct AsyncList<'a, Cn, Cl, PrimaryKey>
where
Cl: schema::Collection,
PrimaryKey: KeyEncoding<Cl::PrimaryKey> + PartialEq + ?Sized,
Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
{
state: ListState<'a, Cn, Cl, PrimaryKey>,
}
impl<'a, Cn, Cl, PrimaryKey> AsyncList<'a, Cn, Cl, PrimaryKey>
where
Cl: schema::Collection,
Cn: AsyncConnection,
PrimaryKey: KeyEncoding<Cl::PrimaryKey> + PartialEq + ?Sized,
Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
{
pub(crate) const fn new(
collection: MaybeOwned<'a, AsyncCollection<'a, Cn, Cl>>,
range: RangeRef<'a, Cl::PrimaryKey, PrimaryKey>,
) -> Self {
Self {
state: ListState::Pending(Some(AsyncListBuilder {
collection,
range,
sort: Sort::Ascending,
limit: None,
})),
}
}
fn builder(&mut self) -> &mut AsyncListBuilder<'a, Cn, Cl, PrimaryKey> {
if let ListState::Pending(Some(builder)) = &mut self.state {
builder
} else {
unreachable!("Attempted to use after retrieving the result")
}
}
pub fn ascending(mut self) -> Self {
self.builder().sort = Sort::Ascending;
self
}
pub fn descending(mut self) -> Self {
self.builder().sort = Sort::Descending;
self
}
pub fn limit(mut self, maximum_results: u32) -> Self {
self.builder().limit = Some(maximum_results);
self
}
pub async fn headers(self) -> Result<Vec<Header>, Error> {
match self.state {
ListState::Pending(Some(AsyncListBuilder {
collection,
range,
sort,
limit,
..
})) => {
collection
.connection
.list_headers::<Cl, _, _>(range, sort, limit)
.await
}
_ => unreachable!("Attempted to use after retrieving the result"),
}
}
pub async fn count(self) -> Result<u64, Error> {
match self.state {
ListState::Pending(Some(AsyncListBuilder {
collection, range, ..
})) => collection.connection.count::<Cl, _, _>(range).await,
_ => unreachable!("Attempted to use after retrieving the result"),
}
}
}
#[allow(clippy::type_repetition_in_bounds)]
impl<'a, Cn, Cl, PrimaryKey> Future for AsyncList<'a, Cn, Cl, PrimaryKey>
where
Cn: AsyncConnection,
Cl: schema::Collection + Unpin,
PrimaryKey: KeyEncoding<Cl::PrimaryKey> + PartialEq + ?Sized + Unpin,
Cl::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey> + Unpin,
{
type Output = Result<Vec<OwnedDocument>, Error>;
fn poll(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
match &mut self.state {
ListState::Executing(future) => future.as_mut().poll(cx),
ListState::Pending(builder) => {
let AsyncListBuilder {
collection,
range,
sort,
limit,
} = builder.take().unwrap();
let future = async move {
collection
.connection
.list::<Cl, _, _>(range, sort, limit)
.await
}
.boxed();
self.state = ListState::Executing(future);
self.poll(cx)
}
}
}
}
#[must_use]
pub struct AsyncView<'a, Cn, V: schema::SerializedView, Key>
where
V::Key: Borrow<Key> + PartialEq<Key>,
Key: PartialEq + ?Sized,
{
connection: &'a Cn,
pub key: Option<QueryKey<'a, V::Key, Key>>,
pub access_policy: AccessPolicy,
pub sort: Sort,
pub limit: Option<u32>,
_view: PhantomData<V>,
}
impl<'a, Cn, V, Key> AsyncView<'a, Cn, V, Key>
where
V: schema::SerializedView,
Cn: AsyncConnection,
Key: KeyEncoding<V::Key> + PartialEq + ?Sized,
V::Key: Borrow<Key> + PartialEq<Key>,
{
const fn new(connection: &'a Cn) -> Self {
Self {
connection,
key: None,
access_policy: AccessPolicy::UpdateBefore,
sort: Sort::Ascending,
limit: None,
_view: PhantomData,
}
}
#[allow(clippy::missing_const_for_fn)] pub fn with_key<K>(self, key: &'a K) -> AsyncView<'a, Cn, V, K>
where
K: KeyEncoding<V::Key> + PartialEq + ?Sized,
V::Key: Borrow<K> + PartialEq<K>,
{
AsyncView {
connection: self.connection,
key: Some(QueryKey::Matches(MaybeOwned::Borrowed(key))),
access_policy: self.access_policy,
sort: self.sort,
limit: self.limit,
_view: PhantomData,
}
}
pub fn with_keys<K, IntoIter: IntoIterator<Item = &'a K>>(
self,
keys: IntoIter,
) -> AsyncView<'a, Cn, V, K>
where
K: PartialEq + ?Sized,
V::Key: Borrow<K> + PartialEq<K>,
{
AsyncView {
connection: self.connection,
key: Some(QueryKey::Multiple(
keys.into_iter().map(MaybeOwned::Borrowed).collect(),
)),
access_policy: self.access_policy,
sort: self.sort,
limit: self.limit,
_view: PhantomData,
}
}
pub fn with_key_range<K, R: Into<RangeRef<'a, V::Key, K>>>(
self,
range: R,
) -> AsyncView<'a, Cn, V, K>
where
K: KeyEncoding<V::Key> + PartialEq + ?Sized,
V::Key: Borrow<K> + PartialEq<K>,
{
AsyncView {
connection: self.connection,
key: Some(QueryKey::Range(range.into())),
access_policy: self.access_policy,
sort: self.sort,
limit: self.limit,
_view: PhantomData,
}
}
pub fn with_key_prefix<K>(self, prefix: &'a K) -> AsyncView<'a, Cn, V, K>
where
K: KeyEncoding<V::Key> + IntoPrefixRange<'a, V::Key> + PartialEq + ?Sized,
V::Key: Borrow<K> + PartialEq<K>,
{
AsyncView {
connection: self.connection,
key: Some(QueryKey::Range(prefix.to_prefix_range())),
access_policy: self.access_policy,
sort: self.sort,
limit: self.limit,
_view: PhantomData,
}
}
pub const fn with_access_policy(mut self, policy: AccessPolicy) -> Self {
self.access_policy = policy;
self
}
pub const fn ascending(mut self) -> Self {
self.sort = Sort::Ascending;
self
}
pub const fn descending(mut self) -> Self {
self.sort = Sort::Descending;
self
}
pub const fn limit(mut self, maximum_results: u32) -> Self {
self.limit = Some(maximum_results);
self
}
pub async fn query(self) -> Result<ViewMappingsCurrent<V>, Error> {
self.connection
.query::<V, Key>(self.key, self.sort, self.limit, self.access_policy)
.await
}
pub async fn query_with_docs(self) -> Result<MappedDocuments<OwnedDocument, V>, Error> {
self.connection
.query_with_docs::<V, _>(self.key, self.sort, self.limit, self.access_policy)
.await
}
pub async fn query_with_collection_docs(
self,
) -> Result<MappedDocuments<CollectionDocument<V::Collection>, V>, Error>
where
V::Collection: SerializedCollection,
<V::Collection as SerializedCollection>::Contents: std::fmt::Debug,
{
self.connection
.query_with_collection_docs::<V, _>(self.key, self.sort, self.limit, self.access_policy)
.await
}
pub async fn reduce(self) -> Result<V::Value, Error> {
self.connection
.reduce::<V, _>(self.key, self.access_policy)
.await
}
pub async fn reduce_grouped(self) -> Result<Vec<MappedValue<V::Key, V::Value>>, Error> {
self.connection
.reduce_grouped::<V, _>(self.key, self.access_policy)
.await
}
pub async fn delete_docs(self) -> Result<u64, Error> {
self.connection
.delete_docs::<V, _>(self.key, self.access_policy)
.await
}
}
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
pub enum Sort {
Ascending,
Descending,
}
#[derive(Clone, Debug)]
pub enum QueryKey<'k, KOwned, KBorrowed = KOwned>
where
KBorrowed: PartialEq + ?Sized,
KOwned: Borrow<KBorrowed> + PartialEq<KBorrowed>,
{
Matches(MaybeOwned<'k, KOwned, KBorrowed>),
Range(RangeRef<'k, KOwned, KBorrowed>),
Multiple(Vec<MaybeOwned<'k, KOwned, KBorrowed>>),
}
impl<'a, KOwned, KBorrowed> QueryKey<'a, KOwned, KBorrowed>
where
KBorrowed: KeyEncoding<KOwned> + PartialEq + ?Sized,
KOwned: for<'k> Key<'k> + Borrow<KBorrowed> + PartialEq<KBorrowed>,
{
pub fn serialized(&self) -> Result<SerializedQueryKey, Error> {
match self {
Self::Matches(key) => key
.as_ord_bytes()
.map_err(|err| Error::other("key serialization", err))
.map(|v| SerializedQueryKey::Matches(Bytes::from(v.to_vec()))),
Self::Range(range) => Ok(SerializedQueryKey::Range(
range
.as_ord_bytes()
.map_err(|err| Error::other("key serialization", err))?,
)),
Self::Multiple(keys) => {
let keys = keys
.iter()
.map(|key| {
key.as_ord_bytes()
.map(|key| Bytes::from(key.to_vec()))
.map_err(|err| Error::other("key serialization", err))
})
.collect::<Result<Vec<_>, Error>>()?;
Ok(SerializedQueryKey::Multiple(keys))
}
}
}
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub enum SerializedQueryKey {
Matches(Bytes),
Range(Range<Bytes>),
Multiple(Vec<Bytes>),
}
impl SerializedQueryKey {
pub fn deserialized<K: for<'k> Key<'k> + PartialEq>(
&self,
) -> Result<QueryKey<'static, K>, Error> {
match self {
Self::Matches(key) => K::from_ord_bytes(ByteSource::Borrowed(key.as_ref()))
.map_err(|err| Error::other("key serialization", err))
.map(|key| QueryKey::Matches(MaybeOwned::Owned(key))),
Self::Range(range) => Ok(QueryKey::Range(RangeRef::owned(
range
.deserialize()
.map_err(|err| Error::other("key serialization", err))?,
))),
Self::Multiple(keys) => {
let keys = keys
.iter()
.map(|key| {
K::from_ord_bytes(ByteSource::Borrowed(key.as_ref()))
.map(MaybeOwned::Owned)
.map_err(|err| Error::other("key serialization", err))
})
.collect::<Result<Vec<_>, Error>>()?;
Ok(QueryKey::Multiple(keys))
}
}
}
}
#[derive(Serialize, Deserialize, Default, Debug, Copy, Clone, Eq, PartialEq)]
#[must_use]
pub struct Range<T> {
pub start: Bound<T>,
pub end: Bound<T>,
}
#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq)]
#[must_use]
pub enum Bound<T> {
Unbounded,
Included(T),
Excluded(T),
}
impl<T> Default for Bound<T> {
fn default() -> Self {
Self::Unbounded
}
}
impl<T> Range<T> {
#[allow(clippy::missing_const_for_fn)]
pub fn after(mut self, excluded_start: T) -> Self {
self.start = Bound::Excluded(excluded_start);
self
}
#[allow(clippy::missing_const_for_fn)]
pub fn start_at(mut self, included_start: T) -> Self {
self.start = Bound::Included(included_start);
self
}
#[allow(clippy::missing_const_for_fn)]
pub fn before(mut self, excluded_end: T) -> Self {
self.end = Bound::Excluded(excluded_end);
self
}
#[allow(clippy::missing_const_for_fn)]
pub fn end_at(mut self, included_end: T) -> Self {
self.end = Bound::Included(included_end);
self
}
pub fn map<U, F: Fn(T) -> U>(self, map: F) -> Range<U> {
Range {
start: self.start.map(&map),
end: self.end.map(&map),
}
}
pub fn map_result<U, E, F: Fn(T) -> Result<U, E>>(self, map: F) -> Result<Range<U>, E> {
Ok(Range {
start: self.start.map_result(&map)?,
end: self.end.map_result(&map)?,
})
}
pub fn map_ref<U: ?Sized, F: Fn(&T) -> &U>(&self, map: F) -> Range<&U> {
Range {
start: self.start.map_ref(&map),
end: self.end.map_ref(&map),
}
}
}
#[test]
fn range_constructors() {
assert_eq!(
Range::default().after(1_u32),
Range {
start: Bound::Excluded(1),
end: Bound::Unbounded
}
);
assert_eq!(
Range::default().start_at(1_u32),
Range {
start: Bound::Included(1),
end: Bound::Unbounded
}
);
assert_eq!(
Range::default().before(1_u32),
Range {
start: Bound::Unbounded,
end: Bound::Excluded(1),
}
);
assert_eq!(
Range::default().end_at(1_u32),
Range {
start: Bound::Unbounded,
end: Bound::Included(1),
}
);
}
impl<'a, TOwned, TBorrowed> RangeRef<'a, TOwned, TBorrowed>
where
TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
TBorrowed: PartialEq + ?Sized,
{
pub fn as_ord_bytes(&'a self) -> Result<Range<Bytes>, TBorrowed::Error>
where
TBorrowed: KeyEncoding<TOwned>,
TOwned: for<'k> Key<'k> + Borrow<TBorrowed>,
{
Ok(Range {
start: self.start.as_ord_bytes()?,
end: self.end.as_ord_bytes()?,
})
}
}
impl Range<Bytes> {
pub fn deserialize<T: for<'k> Key<'k>>(
&self,
) -> Result<Range<T>, <T as KeyEncoding<T>>::Error> {
Ok(Range {
start: self.start.deserialize()?,
end: self.start.deserialize()?,
})
}
}
impl<T> Bound<T> {
pub fn map<U, F: Fn(T) -> U>(self, map: F) -> Bound<U> {
match self {
Self::Unbounded => Bound::Unbounded,
Self::Included(value) => Bound::Included(map(value)),
Self::Excluded(value) => Bound::Excluded(map(value)),
}
}
pub fn map_result<U, E, F: Fn(T) -> Result<U, E>>(self, map: F) -> Result<Bound<U>, E> {
Ok(match self {
Self::Unbounded => Bound::Unbounded,
Self::Included(value) => Bound::Included(map(value)?),
Self::Excluded(value) => Bound::Excluded(map(value)?),
})
}
pub fn map_ref<U: ?Sized, F: Fn(&T) -> &U>(&self, map: F) -> Bound<&U> {
match self {
Self::Unbounded => Bound::Unbounded,
Self::Included(value) => Bound::Included(map(value)),
Self::Excluded(value) => Bound::Excluded(map(value)),
}
}
}
impl<'a, TOwned, TBorrowed> BoundRef<'a, TOwned, TBorrowed>
where
TBorrowed: PartialEq + ?Sized,
TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
{
pub fn as_ord_bytes(&'a self) -> Result<Bound<Bytes>, TBorrowed::Error>
where
TBorrowed: KeyEncoding<TOwned>,
TOwned: for<'k> Key<'k> + Borrow<TBorrowed>,
{
match self {
Self::Unbounded => Ok(Bound::Unbounded),
Self::Included(value) => {
Ok(Bound::Included(Bytes::from(value.as_ord_bytes()?.to_vec())))
}
Self::Excluded(value) => {
Ok(Bound::Excluded(Bytes::from(value.as_ord_bytes()?.to_vec())))
}
}
}
}
impl Bound<Bytes> {
pub fn deserialize<T: for<'k> Key<'k>>(
&self,
) -> Result<Bound<T>, <T as KeyEncoding<T>>::Error> {
match self {
Self::Unbounded => Ok(Bound::Unbounded),
Self::Included(value) => Ok(Bound::Included(T::from_ord_bytes(ByteSource::Borrowed(
value.as_ref(),
))?)),
Self::Excluded(value) => Ok(Bound::Excluded(T::from_ord_bytes(ByteSource::Borrowed(
value.as_ref(),
))?)),
}
}
}
impl<T> std::ops::RangeBounds<T> for Range<T> {
fn start_bound(&self) -> std::ops::Bound<&T> {
std::ops::Bound::from(&self.start)
}
fn end_bound(&self) -> std::ops::Bound<&T> {
std::ops::Bound::from(&self.end)
}
}
impl<'a, T> From<&'a Bound<T>> for std::ops::Bound<&'a T> {
fn from(bound: &'a Bound<T>) -> Self {
match bound {
Bound::Unbounded => std::ops::Bound::Unbounded,
Bound::Included(value) => std::ops::Bound::Included(value),
Bound::Excluded(value) => std::ops::Bound::Excluded(value),
}
}
}
impl<'a, T> From<Bound<&'a T>> for std::ops::Bound<&'a T> {
fn from(bound: Bound<&'a T>) -> Self {
match bound {
Bound::Unbounded => std::ops::Bound::Unbounded,
Bound::Included(value) => std::ops::Bound::Included(value),
Bound::Excluded(value) => std::ops::Bound::Excluded(value),
}
}
}
impl<T> From<std::ops::Range<T>> for Range<T> {
fn from(range: std::ops::Range<T>) -> Self {
Self {
start: Bound::Included(range.start),
end: Bound::Excluded(range.end),
}
}
}
impl<T> From<std::ops::RangeFrom<T>> for Range<T> {
fn from(range: std::ops::RangeFrom<T>) -> Self {
Self {
start: Bound::Included(range.start),
end: Bound::Unbounded,
}
}
}
impl<T> From<std::ops::RangeTo<T>> for Range<T> {
fn from(range: std::ops::RangeTo<T>) -> Self {
Self {
start: Bound::Unbounded,
end: Bound::Excluded(range.end),
}
}
}
impl<T: Clone> From<std::ops::RangeInclusive<T>> for Range<T> {
fn from(range: std::ops::RangeInclusive<T>) -> Self {
Self {
start: Bound::Included(range.start().clone()),
end: Bound::Included(range.end().clone()),
}
}
}
impl<T> From<std::ops::RangeToInclusive<T>> for Range<T> {
fn from(range: std::ops::RangeToInclusive<T>) -> Self {
Self {
start: Bound::Unbounded,
end: Bound::Included(range.end),
}
}
}
impl<T> From<std::ops::RangeFull> for Range<T> {
fn from(_: std::ops::RangeFull) -> Self {
Self {
start: Bound::Unbounded,
end: Bound::Unbounded,
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[must_use]
pub struct RangeRef<'a, TOwned, TBorrowed = TOwned>
where
TBorrowed: PartialEq + ?Sized,
TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
{
pub start: BoundRef<'a, TOwned, TBorrowed>,
pub end: BoundRef<'a, TOwned, TBorrowed>,
}
impl<'a, TOwned, TBorrowed> From<std::ops::Range<TOwned>> for RangeRef<'a, TOwned, TBorrowed>
where
TBorrowed: PartialEq,
TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
{
fn from(range: std::ops::Range<TOwned>) -> Self {
Self {
start: BoundRef::Included(MaybeOwned::Owned(range.start)),
end: BoundRef::Excluded(MaybeOwned::Owned(range.end)),
}
}
}
impl<'a, 'b, TOwned, TBorrowed> From<&'b std::ops::Range<&'a TBorrowed>>
for RangeRef<'a, TOwned, TBorrowed>
where
TBorrowed: PartialEq,
TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
{
fn from(range: &'b std::ops::Range<&'a TBorrowed>) -> Self {
Self {
start: BoundRef::Included(MaybeOwned::Borrowed(range.start)),
end: BoundRef::Excluded(MaybeOwned::Borrowed(range.end)),
}
}
}
impl<'a, TOwned, TBorrowed> From<std::ops::RangeFrom<TOwned>> for RangeRef<'a, TOwned, TBorrowed>
where
TBorrowed: PartialEq,
TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
{
fn from(range: std::ops::RangeFrom<TOwned>) -> Self {
Self {
start: BoundRef::Included(MaybeOwned::Owned(range.start)),
end: BoundRef::Unbounded,
}
}
}
impl<'a, 'b, TOwned, TBorrowed> From<&'b std::ops::RangeFrom<&'a TBorrowed>>
for RangeRef<'a, TOwned, TBorrowed>
where
TBorrowed: PartialEq,
TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
{
fn from(range: &'b std::ops::RangeFrom<&'a TBorrowed>) -> Self {
Self {
start: BoundRef::Included(MaybeOwned::Borrowed(range.start)),
end: BoundRef::Unbounded,
}
}
}
impl<'a, TOwned, TBorrowed> From<std::ops::RangeTo<TOwned>> for RangeRef<'a, TOwned, TBorrowed>
where
TBorrowed: PartialEq,
TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
{
fn from(range: std::ops::RangeTo<TOwned>) -> Self {
Self {
start: BoundRef::Unbounded,
end: BoundRef::Excluded(MaybeOwned::Owned(range.end)),
}
}
}
impl<'a, 'b, TOwned, TBorrowed> From<&'b std::ops::RangeTo<&'a TBorrowed>>
for RangeRef<'a, TOwned, TBorrowed>
where
TBorrowed: PartialEq,
TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
{
fn from(range: &'b std::ops::RangeTo<&'a TBorrowed>) -> Self {
Self {
start: BoundRef::Unbounded,
end: BoundRef::Excluded(MaybeOwned::Borrowed(range.end)),
}
}
}
impl<'a, TOwned, TBorrowed> From<std::ops::RangeInclusive<TOwned>>
for RangeRef<'a, TOwned, TBorrowed>
where
TBorrowed: PartialEq,
TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed> + Clone,
{
fn from(range: std::ops::RangeInclusive<TOwned>) -> Self {
Self {
start: BoundRef::Included(MaybeOwned::Owned(range.start().clone())),
end: BoundRef::Included(MaybeOwned::Owned(range.end().clone())),
}
}
}
impl<'a, 'b, TOwned, TBorrowed> From<&'b std::ops::RangeInclusive<&'a TBorrowed>>
for RangeRef<'a, TOwned, TBorrowed>
where
TBorrowed: PartialEq,
TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
{
fn from(range: &'b std::ops::RangeInclusive<&'a TBorrowed>) -> Self {
Self {
start: BoundRef::Included(MaybeOwned::Borrowed(range.start())),
end: BoundRef::Included(MaybeOwned::Borrowed(range.end())),
}
}
}
impl<'a, TOwned, TBorrowed> From<std::ops::RangeToInclusive<&'a TBorrowed>>
for RangeRef<'a, TOwned, TBorrowed>
where
TBorrowed: PartialEq,
TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
{
fn from(range: std::ops::RangeToInclusive<&'a TBorrowed>) -> Self {
Self {
start: BoundRef::Unbounded,
end: BoundRef::Included(MaybeOwned::Borrowed(range.end)),
}
}
}
impl<'a, TOwned, TBorrowed> From<std::ops::RangeFull> for RangeRef<'a, TOwned, TBorrowed>
where
TBorrowed: PartialEq,
TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
{
fn from(_: std::ops::RangeFull) -> Self {
Self {
start: BoundRef::Unbounded,
end: BoundRef::Unbounded,
}
}
}
impl<'a, TOwned, TBorrowed> RangeRef<'a, TOwned, TBorrowed>
where
TBorrowed: PartialEq + ?Sized,
TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
{
pub const fn borrowed(range: Range<&'a TBorrowed>) -> Self {
Self {
start: BoundRef::borrowed(range.start),
end: BoundRef::borrowed(range.end),
}
}
pub fn owned(range: Range<TOwned>) -> Self {
Self {
start: BoundRef::owned(range.start),
end: BoundRef::owned(range.end),
}
}
pub fn map_result<U, E, F: Fn(&TBorrowed) -> Result<U, E>>(
self,
map: F,
) -> Result<Range<U>, E> {
Ok(Range {
start: self.start.map_result(&map)?,
end: self.end.map_result(&map)?,
})
}
}
impl<'a, TOwned, TBorrowed> std::ops::RangeBounds<TBorrowed> for RangeRef<'a, TOwned, TBorrowed>
where
TBorrowed: PartialEq + ?Sized,
TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
{
fn start_bound(&self) -> std::ops::Bound<&TBorrowed> {
std::ops::Bound::from(&self.start)
}
fn end_bound(&self) -> std::ops::Bound<&TBorrowed> {
std::ops::Bound::from(&self.end)
}
}
impl<'a, TOwned, TBorrowed> From<&'a BoundRef<'a, TOwned, TBorrowed>>
for std::ops::Bound<&'a TBorrowed>
where
TBorrowed: PartialEq + ?Sized,
TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
{
fn from(bound: &'a BoundRef<'a, TOwned, TBorrowed>) -> Self {
match bound {
BoundRef::Unbounded => std::ops::Bound::Unbounded,
BoundRef::Included(value) => std::ops::Bound::Included(value),
BoundRef::Excluded(value) => std::ops::Bound::Excluded(value),
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[must_use]
pub enum BoundRef<'a, TOwned, TBorrowed = TOwned>
where
TBorrowed: PartialEq + ?Sized,
TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
MaybeOwned<'a, TOwned, TBorrowed>: PartialEq,
{
Unbounded,
Included(MaybeOwned<'a, TOwned, TBorrowed>),
Excluded(MaybeOwned<'a, TOwned, TBorrowed>),
}
impl<'a, TOwned, TBorrowed> BoundRef<'a, TOwned, TBorrowed>
where
TBorrowed: PartialEq + ?Sized,
TOwned: Borrow<TBorrowed> + PartialEq<TBorrowed>,
{
pub const fn borrowed(range: Bound<&'a TBorrowed>) -> Self {
match range {
Bound::Unbounded => Self::Unbounded,
Bound::Included(value) => Self::Included(MaybeOwned::Borrowed(value)),
Bound::Excluded(value) => Self::Excluded(MaybeOwned::Borrowed(value)),
}
}
#[allow(clippy::missing_const_for_fn)] pub fn owned(range: Bound<TOwned>) -> Self {
match range {
Bound::Unbounded => Self::Unbounded,
Bound::Included(value) => Self::Included(MaybeOwned::Owned(value)),
Bound::Excluded(value) => Self::Excluded(MaybeOwned::Owned(value)),
}
}
pub fn map_result<U, E, F: Fn(&TBorrowed) -> Result<U, E>>(
self,
map: F,
) -> Result<Bound<U>, E> {
Ok(match self {
BoundRef::Unbounded => Bound::Unbounded,
BoundRef::Included(value) => Bound::Included(map(&*value)?),
BoundRef::Excluded(value) => Bound::Excluded(map(&*value)?),
})
}
}
#[derive(Copy, Clone, Serialize, Deserialize, Debug)]
pub enum AccessPolicy {
UpdateBefore,
UpdateAfter,
NoUpdate,
}
#[async_trait]
pub trait StorageConnection: HasSession + Sized + Send + Sync {
type Database: Connection;
type Authenticated: StorageConnection;
fn admin(&self) -> Self::Database;
fn create_database<DB: Schema>(
&self,
name: &str,
only_if_needed: bool,
) -> Result<Self::Database, crate::Error> {
self.create_database_with_schema(name, DB::schema_name(), only_if_needed)?;
self.database::<DB>(name)
}
fn database<DB: Schema>(&self, name: &str) -> Result<Self::Database, crate::Error>;
fn create_database_with_schema(
&self,
name: &str,
schema: SchemaName,
only_if_needed: bool,
) -> Result<(), crate::Error>;
fn delete_database(&self, name: &str) -> Result<(), crate::Error>;
fn list_databases(&self) -> Result<Vec<Database>, crate::Error>;
fn list_available_schemas(&self) -> Result<Vec<SchemaSummary>, crate::Error>;
fn create_user(&self, username: &str) -> Result<u64, crate::Error>;
fn delete_user<'user, U: Nameable<'user, u64> + Send + Sync>(
&self,
user: U,
) -> Result<(), crate::Error>;
#[cfg(feature = "password-hashing")]
fn set_user_password<'user, U: Nameable<'user, u64> + Send + Sync>(
&self,
user: U,
password: SensitiveString,
) -> Result<(), crate::Error>;
#[cfg(any(feature = "token-authentication", feature = "password-hashing"))]
fn authenticate(
&self,
authentication: Authentication,
) -> Result<Self::Authenticated, crate::Error>;
fn assume_identity(
&self,
identity: IdentityReference<'_>,
) -> Result<Self::Authenticated, crate::Error>;
#[cfg(feature = "token-authentication")]
fn authenticate_with_token(
&self,
id: u64,
token: &SensitiveString,
) -> Result<<Self::Authenticated as StorageConnection>::Authenticated, crate::Error> {
let challenge_session = self.authenticate(Authentication::token(id, token)?)?;
match challenge_session
.session()
.map(|session| &session.authentication)
{
Some(SessionAuthentication::TokenChallenge {
algorithm: TokenChallengeAlgorithm::Blake3,
nonce,
server_timestamp,
..
}) => {
let response = crate::admin::AuthenticationToken::compute_challenge_response_blake3(
token,
nonce,
*server_timestamp,
);
challenge_session.authenticate(Authentication::TokenChallengeResponse(Bytes::from(
response.as_bytes().to_vec(),
)))
}
_ => Err(crate::Error::InvalidCredentials),
}
}
#[cfg(feature = "password-hashing")]
fn authenticate_with_password<'name, User: Nameable<'name, u64>>(
&self,
user: User,
password: SensitiveString,
) -> Result<Self::Authenticated, crate::Error> {
self.authenticate(Authentication::password(user, password)?)
}
fn add_permission_group_to_user<
'user,
'group,
U: Nameable<'user, u64> + Send + Sync,
G: Nameable<'group, u64> + Send + Sync,
>(
&self,
user: U,
permission_group: G,
) -> Result<(), crate::Error>;
fn remove_permission_group_from_user<
'user,
'group,
U: Nameable<'user, u64> + Send + Sync,
G: Nameable<'group, u64> + Send + Sync,
>(
&self,
user: U,
permission_group: G,
) -> Result<(), crate::Error>;
fn add_role_to_user<
'user,
'role,
U: Nameable<'user, u64> + Send + Sync,
R: Nameable<'role, u64> + Send + Sync,
>(
&self,
user: U,
role: R,
) -> Result<(), crate::Error>;
fn remove_role_from_user<
'user,
'role,
U: Nameable<'user, u64> + Send + Sync,
R: Nameable<'role, u64> + Send + Sync,
>(
&self,
user: U,
role: R,
) -> Result<(), crate::Error>;
}
#[async_trait]
pub trait AsyncStorageConnection: HasSession + Sized + Send + Sync {
type Database: AsyncConnection;
type Authenticated: AsyncStorageConnection;
async fn admin(&self) -> Self::Database;
async fn create_database<DB: Schema>(
&self,
name: &str,
only_if_needed: bool,
) -> Result<Self::Database, crate::Error> {
self.create_database_with_schema(name, DB::schema_name(), only_if_needed)
.await?;
self.database::<DB>(name).await
}
async fn database<DB: Schema>(&self, name: &str) -> Result<Self::Database, crate::Error>;
async fn create_database_with_schema(
&self,
name: &str,
schema: SchemaName,
only_if_needed: bool,
) -> Result<(), crate::Error>;
async fn delete_database(&self, name: &str) -> Result<(), crate::Error>;
async fn list_databases(&self) -> Result<Vec<Database>, crate::Error>;
async fn list_available_schemas(&self) -> Result<Vec<SchemaSummary>, crate::Error>;
async fn create_user(&self, username: &str) -> Result<u64, crate::Error>;
async fn delete_user<'user, U: Nameable<'user, u64> + Send + Sync>(
&self,
user: U,
) -> Result<(), crate::Error>;
#[cfg(feature = "password-hashing")]
async fn set_user_password<'user, U: Nameable<'user, u64> + Send + Sync>(
&self,
user: U,
password: SensitiveString,
) -> Result<(), crate::Error>;
#[cfg(any(feature = "token-authentication", feature = "password-hashing"))]
async fn authenticate(
&self,
authentication: Authentication,
) -> Result<Self::Authenticated, crate::Error>;
#[cfg(feature = "token-authentication")]
async fn authenticate_with_token(
&self,
id: u64,
token: &SensitiveString,
) -> Result<<Self::Authenticated as AsyncStorageConnection>::Authenticated, crate::Error> {
let challenge_session = self.authenticate(Authentication::token(id, token)?).await?;
match challenge_session
.session()
.map(|session| &session.authentication)
{
Some(SessionAuthentication::TokenChallenge {
algorithm: TokenChallengeAlgorithm::Blake3,
nonce,
server_timestamp,
..
}) => {
let response = crate::admin::AuthenticationToken::compute_challenge_response_blake3(
token,
nonce,
*server_timestamp,
);
challenge_session
.authenticate(Authentication::TokenChallengeResponse(Bytes::from(
response.as_bytes().to_vec(),
)))
.await
}
_ => Err(crate::Error::InvalidCredentials),
}
}
#[cfg(feature = "password-hashing")]
async fn authenticate_with_password<'name, User: Nameable<'name, u64> + Send>(
&self,
user: User,
password: SensitiveString,
) -> Result<Self::Authenticated, crate::Error> {
self.authenticate(Authentication::password(user, password)?)
.await
}
async fn assume_identity(
&self,
identity: IdentityReference<'_>,
) -> Result<Self::Authenticated, crate::Error>;
async fn add_permission_group_to_user<
'user,
'group,
U: Nameable<'user, u64> + Send + Sync,
G: Nameable<'group, u64> + Send + Sync,
>(
&self,
user: U,
permission_group: G,
) -> Result<(), crate::Error>;
async fn remove_permission_group_from_user<
'user,
'group,
U: Nameable<'user, u64> + Send + Sync,
G: Nameable<'group, u64> + Send + Sync,
>(
&self,
user: U,
permission_group: G,
) -> Result<(), crate::Error>;
async fn add_role_to_user<
'user,
'role,
U: Nameable<'user, u64> + Send + Sync,
R: Nameable<'role, u64> + Send + Sync,
>(
&self,
user: U,
role: R,
) -> Result<(), crate::Error>;
async fn remove_role_from_user<
'user,
'role,
U: Nameable<'user, u64> + Send + Sync,
R: Nameable<'role, u64> + Send + Sync,
>(
&self,
user: U,
role: R,
) -> Result<(), crate::Error>;
}
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
pub struct Database {
pub name: String,
pub schema: SchemaName,
}
#[derive(Clone, Default, Serialize, Deserialize, Zeroize, Eq, PartialEq)]
#[zeroize(drop)]
#[serde(transparent)]
pub struct SensitiveString(pub String);
impl std::fmt::Debug for SensitiveString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("SensitiveString(...)")
}
}
impl Deref for SensitiveString {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for SensitiveString {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<'k> Key<'k> for SensitiveString {
const CAN_OWN_BYTES: bool = String::CAN_OWN_BYTES;
fn from_ord_bytes<'e>(bytes: ByteSource<'k, 'e>) -> Result<Self, Self::Error> {
String::from_ord_bytes(bytes).map(Self)
}
}
impl KeyEncoding<Self> for SensitiveString {
type Error = FromUtf8Error;
const LENGTH: Option<usize> = None;
fn describe<Visitor>(visitor: &mut Visitor)
where
Visitor: KeyVisitor,
{
visitor.visit_type(KeyKind::String);
}
fn as_ord_bytes(&self) -> Result<std::borrow::Cow<'_, [u8]>, Self::Error> {
self.0.as_ord_bytes()
}
}
impl From<String> for SensitiveString {
fn from(sensitive: String) -> Self {
Self(sensitive)
}
}
impl<'a> From<&'a str> for SensitiveString {
fn from(sensitive: &'a str) -> Self {
Self(sensitive.to_owned())
}
}
#[derive(Clone, Serialize, Deserialize, Zeroize, Eq, PartialEq)]
#[zeroize(drop)]
#[serde(transparent)]
pub struct SensitiveBytes(pub Bytes);
impl std::fmt::Debug for SensitiveBytes {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("SensitiveBytes(...)")
}
}
impl Deref for SensitiveBytes {
type Target = Bytes;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for SensitiveBytes {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<'k> Key<'k> for SensitiveBytes {
const CAN_OWN_BYTES: bool = Bytes::CAN_OWN_BYTES;
fn from_ord_bytes<'e>(bytes: ByteSource<'k, 'e>) -> Result<Self, Self::Error> {
Bytes::from_ord_bytes(bytes).map(Self)
}
}
impl KeyEncoding<Self> for SensitiveBytes {
type Error = Infallible;
const LENGTH: Option<usize> = None;
fn describe<Visitor>(visitor: &mut Visitor)
where
Visitor: KeyVisitor,
{
visitor.visit_type(KeyKind::Bytes);
}
fn as_ord_bytes(&self) -> Result<std::borrow::Cow<'_, [u8]>, Self::Error> {
self.0.as_ord_bytes()
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[must_use]
pub enum Authentication {
#[cfg(feature = "token-authentication")]
Token {
id: u64,
now: crate::key::time::TimestampAsNanoseconds,
now_hash: Bytes,
algorithm: TokenChallengeAlgorithm,
},
#[cfg(feature = "token-authentication")]
TokenChallengeResponse(Bytes),
#[cfg(feature = "password-hashing")]
Password {
user: NamedReference<'static, u64>,
password: SensitiveString,
},
}
impl Authentication {
#[cfg(feature = "password-hashing")]
pub fn password<'user, UsernameOrId: Nameable<'user, u64>>(
user: UsernameOrId,
password: SensitiveString,
) -> Result<Self, crate::Error> {
Ok(Self::Password {
user: user.name()?.into_owned(),
password,
})
}
#[cfg(feature = "token-authentication")]
pub fn token(id: u64, token: &SensitiveString) -> Result<Self, crate::Error> {
let now = crate::key::time::TimestampAsNanoseconds::now();
Ok(Self::Token {
id,
now,
now_hash: Bytes::from(
crate::admin::AuthenticationToken::compute_request_time_hash_blake3(now, token)
.as_bytes()
.to_vec(),
),
algorithm: TokenChallengeAlgorithm::Blake3,
})
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! __doctest_prelude {
() => {
use bonsaidb_core::{
connection::AccessPolicy,
define_basic_unique_mapped_view,
document::{CollectionDocument,Emit, Document, OwnedDocument},
schema::{
Collection, CollectionName, DefaultSerialization,
DefaultViewSerialization, Name, NamedCollection, ReduceResult, Schema, SchemaName,
Schematic, SerializedCollection, View, ViewSchema, CollectionMapReduce, ViewMapResult, ViewMappedValue, SerializedView,
},
Error,
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Schema)]
#[schema(name = "MySchema", collections = [MyCollection], core = $crate)]
pub struct MySchema;
#[derive( Debug, Serialize, Deserialize, Default, Collection)]
#[collection(name = "MyCollection", views = [MyCollectionByName], core = $crate)]
pub struct MyCollection {
pub name: String,
pub rank: u32,
pub score: f32,
}
impl MyCollection {
pub fn named(s: impl Into<String>) -> Self {
Self::new(s, 0, 0.)
}
pub fn new(s: impl Into<String>, rank: u32, score: f32) -> Self {
Self {
name: s.into(),
rank,
score,
}
}
}
impl NamedCollection for MyCollection {
type ByNameView = MyCollectionByName;
}
#[derive(Debug, Clone, View, ViewSchema)]
#[view(collection = MyCollection, key = u32, value = f32, name = "scores-by-rank", core = $crate)]
#[view_schema(core = $crate)]
pub struct ScoresByRank;
impl CollectionMapReduce for ScoresByRank {
fn map<'doc>(
&self,
document: CollectionDocument<<Self::View as View>::Collection>,
) -> ViewMapResult<'doc, Self::View> {
document
.header
.emit_key_and_value(document.contents.rank, document.contents.score)
}
fn reduce(
&self,
mappings: &[ViewMappedValue<'_, Self::View>],
rereduce: bool,
) -> ReduceResult<Self::View> {
if mappings.is_empty() {
Ok(0.)
} else {
Ok(mappings.iter().map(|map| map.value).sum::<f32>() / mappings.len() as f32)
}
}
}
define_basic_unique_mapped_view!(
MyCollectionByName,
MyCollection,
1,
"by-name",
String,
(),
|document: CollectionDocument<MyCollection>| {
document.header.emit_key(document.contents.name.clone())
},
);
};
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
#[must_use]
pub struct Session {
pub id: Option<SessionId>,
pub authentication: SessionAuthentication,
pub permissions: Permissions,
}
#[derive(Hash, Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
pub enum SessionAuthentication {
None,
Identity(Arc<Identity>),
#[cfg(feature = "token-authentication")]
TokenChallenge {
id: u64,
algorithm: TokenChallengeAlgorithm,
nonce: [u8; 32],
server_timestamp: crate::key::time::TimestampAsNanoseconds,
},
}
impl Default for SessionAuthentication {
fn default() -> Self {
Self::None
}
}
#[derive(Hash, Eq, PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
#[non_exhaustive]
#[cfg(feature = "token-authentication")]
pub enum TokenChallengeAlgorithm {
Blake3,
}
#[derive(Action, Serialize, Deserialize, Clone, Copy, Debug)]
pub enum AuthenticationMethod {
Token,
PasswordHash,
}
#[derive(Default, Clone, Copy, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)]
#[serde(transparent)]
pub struct SessionId(pub u64);
impl Session {
pub fn allowed_to<'a, R: AsRef<[Identifier<'a>]>, P: Action>(
&self,
resource_name: R,
action: &P,
) -> bool {
self.permissions.allowed_to(resource_name, action)
}
pub fn check_permission<'a, R: AsRef<[Identifier<'a>]>, P: Action>(
&self,
resource_name: R,
action: &P,
) -> Result<(), Error> {
self.permissions
.check(resource_name, action)
.map_err(Error::from)
}
#[must_use]
pub fn identity(&self) -> Option<&Identity> {
if let SessionAuthentication::Identity(identity) = &self.authentication {
Some(identity)
} else {
None
}
}
}
impl Eq for Session {}
impl PartialEq for Session {
fn eq(&self, other: &Self) -> bool {
self.authentication == other.authentication
}
}
impl std::hash::Hash for Session {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.authentication.hash(state);
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Identity {
User {
id: u64,
username: String,
},
Role {
id: u64,
name: String,
},
}
impl Eq for Identity {}
impl PartialEq for Identity {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::User { id: l_id, .. }, Self::User { id: r_id, .. })
| (Self::Role { id: l_id, .. }, Self::Role { id: r_id, .. }) => l_id == r_id,
_ => false,
}
}
}
impl std::hash::Hash for Identity {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
Identity::User { id, .. } => {
0_u8.hash(state); id.hash(state);
}
Identity::Role { id, .. } => {
1_u8.hash(state); id.hash(state);
}
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub enum IdentityReference<'name> {
User(NamedReference<'name, u64>),
Role(NamedReference<'name, u64>),
}
impl<'name> IdentityReference<'name> {
pub fn user<User: Nameable<'name, u64>>(user: User) -> Result<Self, crate::Error> {
Ok(Self::User(user.name()?))
}
pub fn role<Role: Nameable<'name, u64>>(role: Role) -> Result<Self, crate::Error> {
Ok(Self::Role(role.name()?))
}
#[must_use]
pub fn into_owned(self) -> IdentityReference<'static> {
match self {
IdentityReference::User(user) => IdentityReference::User(user.into_owned()),
IdentityReference::Role(role) => IdentityReference::Role(role.into_owned()),
}
}
pub fn resolve<C: Connection>(&self, admin: &C) -> Result<Option<IdentityId>, crate::Error> {
match self {
IdentityReference::User(name) => Ok(name.id::<User, _>(admin)?.map(IdentityId::User)),
IdentityReference::Role(name) => Ok(name.id::<Role, _>(admin)?.map(IdentityId::Role)),
}
}
pub async fn resolve_async<C: AsyncConnection>(
&self,
admin: &C,
) -> Result<Option<IdentityId>, crate::Error> {
match self {
IdentityReference::User(name) => {
Ok(name.id_async::<User, _>(admin).await?.map(IdentityId::User))
}
IdentityReference::Role(name) => {
Ok(name.id_async::<Role, _>(admin).await?.map(IdentityId::Role))
}
}
}
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub enum IdentityId {
User(u64),
Role(u64),
}
#[deprecated(
since = "0.5.0",
note = "ViewMappings has been moved to bonsaidb_core::schema::view::ViewMappings"
)]
pub type ViewMappings<V> = schema::view::map::ViewMappings<V>;