use std::fmt::Debug;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use transmog::{Format, OwnedDeserializer};
use transmog_pot::Pot;
use crate::connection::{self, AsyncConnection, Connection};
use crate::document::{BorrowedDocument, CollectionDocument};
use crate::key::{ByteSource, Key, KeyDescription};
use crate::schema::view::map::{MappedValue, Mappings, ViewMappedValue};
use crate::schema::{Collection, CollectionName, Name, SerializedCollection, ViewName};
use crate::AnyError;
pub mod map;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("error serializing view keys {0}")]
KeySerialization(Box<dyn AnyError>),
#[error("core error: {0}")]
Core(#[from] crate::Error),
}
impl Error {
pub fn key_serialization<E: AnyError>(error: E) -> Self {
Self::KeySerialization(Box::new(error))
}
}
impl From<pot::Error> for Error {
fn from(err: pot::Error) -> Self {
Self::Core(crate::Error::from(err))
}
}
pub type ViewMapResult<'doc, V> = Result<
Mappings<<V as ViewSchema>::MappedKey<'doc>, <<V as ViewSchema>::View as View>::Value>,
crate::Error,
>;
pub type ReduceResult<V> = Result<<V as View>::Value, crate::Error>;
#[doc = "\n"]
#[doc = include_str!("./view-overview.md")]
pub trait View: Sized + Send + Sync + 'static {
type Collection: Collection;
type Key: for<'k> Key<'k> + PartialEq + 'static;
type Value: Send + Sync;
fn name(&self) -> Name;
fn view_name(&self) -> ViewName {
ViewName {
collection: Self::Collection::collection_name(),
name: self.name(),
}
}
}
#[doc = "\n"]
#[doc = include_str!("./view-overview.md")]
pub trait ViewSchema: Send + Sync + 'static {
type View: SerializedView;
type MappedKey<'doc>: Key<'doc>;
fn update_policy(&self) -> ViewUpdatePolicy {
ViewUpdatePolicy::default()
}
fn version(&self) -> u64 {
0
}
}
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum ViewUpdatePolicy {
#[default]
Lazy,
Eager,
Unique,
}
impl ViewUpdatePolicy {
#[must_use]
pub const fn is_eager(&self) -> bool {
matches!(self, Self::Eager | Self::Unique)
}
}
impl std::fmt::Display for ViewUpdatePolicy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Debug::fmt(self, f)
}
}
pub trait MapReduce: ViewSchema {
fn map<'doc>(&self, document: &'doc BorrowedDocument<'_>) -> ViewMapResult<'doc, Self>;
#[allow(unused_variables)]
fn reduce(
&self,
mappings: &[MappedValue<Self::MappedKey<'_>, <Self::View as View>::Value>],
rereduce: bool,
) -> Result<<Self::View as View>::Value, crate::Error> {
Err(crate::Error::ReduceUnimplemented)
}
}
pub trait SerializedView: View {
type Format: OwnedDeserializer<Self::Value>;
fn format() -> Self::Format;
fn deserialize(data: &[u8]) -> Result<Self::Value, crate::Error> {
Self::format()
.deserialize_owned(data)
.map_err(|err| crate::Error::other("serialization", err))
}
fn serialize(item: &Self::Value) -> Result<Vec<u8>, crate::Error> {
Self::format()
.serialize(item)
.map_err(|err| crate::Error::other("serialization", err.to_string()))
}
fn entries<Database: Connection>(
database: &Database,
) -> connection::View<'_, Database, Self, Self::Key> {
database.view::<Self>()
}
fn entries_async<Database: AsyncConnection>(
database: &Database,
) -> connection::AsyncView<'_, Database, Self, Self::Key> {
database.view::<Self>()
}
}
pub trait DefaultViewSerialization: View {}
impl<T> SerializedView for T
where
T: DefaultViewSerialization,
T::Value: Serialize + DeserializeOwned,
{
type Format = Pot;
fn format() -> Self::Format {
Pot::default()
}
}
pub trait CollectionMapReduce: ViewSchema
where
<Self::View as View>::Collection: SerializedCollection,
{
fn map<'doc>(
&self,
document: CollectionDocument<<Self::View as View>::Collection>,
) -> ViewMapResult<'doc, Self>
where
CollectionDocument<<Self::View as View>::Collection>: 'doc;
#[allow(unused_variables)]
fn reduce(
&self,
mappings: &[ViewMappedValue<'_, Self>],
rereduce: bool,
) -> ReduceResult<Self::View> {
Err(crate::Error::ReduceUnimplemented)
}
}
impl<T> MapReduce for T
where
T: CollectionMapReduce,
T::View: SerializedView,
<T::View as View>::Collection: SerializedCollection,
{
fn map<'doc>(&self, document: &'doc BorrowedDocument<'_>) -> ViewMapResult<'doc, Self> {
T::map(self, CollectionDocument::try_from(document)?)
}
fn reduce(
&self,
mappings: &[ViewMappedValue<'_, Self>],
rereduce: bool,
) -> Result<<Self::View as View>::Value, crate::Error> {
T::reduce(self, mappings, rereduce)
}
}
pub trait Serialized: Send + Sync {
fn collection(&self) -> CollectionName;
fn key_description(&self) -> KeyDescription;
fn update_policy(&self) -> ViewUpdatePolicy;
fn version(&self) -> u64;
fn view_name(&self) -> ViewName;
fn map(&self, document: &BorrowedDocument<'_>) -> Result<Vec<map::Serialized>, Error>;
fn reduce(&self, mappings: &[(&[u8], &[u8])], rereduce: bool) -> Result<Vec<u8>, Error>;
}
#[macro_export(local_inner_macros)]
macro_rules! define_basic_unique_mapped_view {
($view_name:ident, $collection:ty, $version:literal, $name:literal, $key:ty, $mapping:expr $(,)?) => {
define_mapped_view!(
$view_name,
$collection,
$version,
$name,
$key,
(),
true,
$mapping
);
};
($view_name:ident, $collection:ty, $version:literal, $name:literal, $key:ty, $value:ty, $mapping:expr $(,)?) => {
define_mapped_view!(
$view_name,
$collection,
$version,
$name,
$key,
$value,
true,
$mapping
);
};
}
#[macro_export(local_inner_macros)]
macro_rules! define_basic_mapped_view {
($view_name:ident, $collection:ty, $version:literal, $name:literal, $key:ty, $mapping:expr $(,)?) => {
define_mapped_view!(
$view_name,
$collection,
$version,
$name,
$key,
(),
false,
$mapping
);
};
($view_name:ident, $collection:ty, $version:literal, $name:literal, $key:ty, $value:ty, $mapping:expr $(,)?) => {
define_mapped_view!(
$view_name,
$collection,
$version,
$name,
$key,
$value,
false,
$mapping
);
};
}
#[macro_export]
macro_rules! define_mapped_view {
($view_name:ident, $collection:ty, $version:literal, $name:literal, $key:ty, $value:ty, $unique:literal, $mapping:expr) => {
#[derive(Debug, Clone)]
pub struct $view_name;
impl $crate::schema::View for $view_name {
type Collection = $collection;
type Key = $key;
type Value = $value;
fn name(&self) -> $crate::schema::Name {
$crate::schema::Name::new($name)
}
}
impl $crate::schema::ViewSchema for $view_name {
type MappedKey<'doc> = <Self as $crate::schema::View>::Key;
type View = Self;
fn update_policy(&self) -> $crate::schema::view::ViewUpdatePolicy {
if $unique {
$crate::schema::view::ViewUpdatePolicy::Unique
} else {
$crate::schema::view::ViewUpdatePolicy::Lazy
}
}
fn version(&self) -> u64 {
$version
}
}
impl $crate::schema::CollectionMapReduce for $view_name {
fn map<'doc>(
&self,
document: $crate::document::CollectionDocument<$collection>,
) -> $crate::schema::ViewMapResult<'doc, Self> {
#[allow(clippy::redundant_closure_call)]
$mapping(document)
}
}
impl $crate::schema::view::DefaultViewSerialization for $view_name {}
};
}