use std::{borrow::Cow, fmt::Debug};
use serde::{Deserialize, Serialize};
use crate::{document::Document, schema::Collection};
pub mod map;
pub use map::{Key, Map};
use self::map::MappedValue;
use super::collection;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("error deserializing document {0}")]
Serialization(#[from] serde_cbor::Error),
#[error("error serializing view keys {0}")]
KeySerialization(anyhow::Error),
#[error("reduce is unimplemented")]
ReduceUnimplemented,
}
pub type MapResult<K = (), V = ()> = Result<Option<Map<K, V>>, Error>;
pub trait View: Send + Sync + Debug + 'static {
type Collection: Collection;
type Key: Key + 'static;
type Value: Serialize + for<'de> Deserialize<'de> + Send + Sync;
fn version(&self) -> u64;
fn name(&self) -> Cow<'static, str>;
fn map(&self, document: &Document<'_>) -> MapResult<Self::Key, Self::Value>;
#[allow(unused_variables)]
fn reduce(
&self,
mappings: &[MappedValue<Self::Key, Self::Value>],
rereduce: bool,
) -> Result<Self::Value, Error> {
Err(Error::ReduceUnimplemented)
}
}
pub enum SerializableValue<'a, T: Serialize> {
Owned(T),
Borrowed(&'a T),
}
impl<'a, T> From<&'a T> for SerializableValue<'a, T>
where
T: Serialize,
{
fn from(other: &'a T) -> SerializableValue<'a, T> {
SerializableValue::Borrowed(other)
}
}
impl<'a, T> AsRef<T> for SerializableValue<'a, T>
where
T: Serialize,
{
fn as_ref(&self) -> &T {
match self {
Self::Owned(value) => value,
Self::Borrowed(value) => value,
}
}
}
pub trait Serialized: Send + Sync + Debug {
fn collection(&self) -> collection::Id;
fn version(&self) -> u64;
fn name(&self) -> Cow<'static, str>;
fn map(&self, document: &Document<'_>) -> Result<Option<map::Serialized>, Error>;
fn reduce(&self, mappings: &[(&[u8], &[u8])], rereduce: bool) -> Result<Vec<u8>, Error>;
}
#[allow(clippy::use_self)] impl<T> Serialized for T
where
T: View,
<T as View>::Key: 'static,
{
fn collection(&self) -> collection::Id {
<<Self as View>::Collection as Collection>::collection_id()
}
fn version(&self) -> u64 {
self.version()
}
fn name(&self) -> Cow<'static, str> {
self.name()
}
fn map(&self, document: &Document<'_>) -> Result<Option<map::Serialized>, Error> {
let map = self.map(document)?;
map.map(|map| map.serialized()).transpose()
}
fn reduce(&self, mappings: &[(&[u8], &[u8])], rereduce: bool) -> Result<Vec<u8>, Error> {
let mappings = mappings
.iter()
.map(
|(key, value)| match <T::Key as Key>::from_big_endian_bytes(key) {
Ok(key) => match serde_cbor::from_slice::<T::Value>(value) {
Ok(value) => Ok(MappedValue { key, value }),
Err(err) => Err(Error::from(err)),
},
Err(err) => Err(Error::KeySerialization(err)),
},
)
.collect::<Result<Vec<_>, Error>>()?;
let reduced_value = match self.reduce(&mappings, rereduce) {
Ok(value) => value,
Err(Error::ReduceUnimplemented) => return Ok(Vec::new()),
Err(other) => return Err(other),
};
serde_cbor::to_vec(&reduced_value).map_err(Error::from)
}
}