Get a better handle on compound views
This commit is contained in:
parent
14b428cc1c
commit
ec0ae92bf5
|
@ -1,8 +1,39 @@
|
|||
use bonsaidb::core::key::{EnumKey, Key, KeyEncoding};
|
||||
use bonsaidb::core::key::{EnumKey, Key};
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::{format_description::well_known::Rfc3339, OffsetDateTime, UtcOffset};
|
||||
use uuid::Uuid;
|
||||
|
||||
mod compound_key;
|
||||
mod datetime_key;
|
||||
mod impossible;
|
||||
mod tagged_key;
|
||||
mod uuid_key;
|
||||
|
||||
pub use compound_key::CompoundKey;
|
||||
pub use datetime_key::DatetimeKey;
|
||||
pub use impossible::Impossible;
|
||||
pub use tagged_key::TaggedKey;
|
||||
pub use uuid_key::UuidKey;
|
||||
|
||||
pub struct Published;
|
||||
pub struct Created;
|
||||
pub struct Updated;
|
||||
pub struct Deleted;
|
||||
|
||||
pub trait KeyExt<'k>: Sized {
|
||||
fn tag<Tag>(self) -> TaggedKey<Self, Tag> {
|
||||
TaggedKey::new(self)
|
||||
}
|
||||
|
||||
fn compound<Secondary>(self, secondary: Secondary) -> CompoundKey<Self, Secondary> {
|
||||
CompoundKey::new(self, secondary)
|
||||
}
|
||||
|
||||
fn into_prefix(self) -> CompoundKey<Self, Impossible> {
|
||||
CompoundKey::prefix(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'k, T> KeyExt<'k> for T where T: Key<'k> {}
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
|
@ -67,95 +98,18 @@ pub enum ReportState {
|
|||
Removed,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct DatetimeKey {
|
||||
#[serde(with = "time::serde::rfc3339")]
|
||||
datetime: OffsetDateTime,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct UuidKey {
|
||||
uuid: Uuid,
|
||||
}
|
||||
|
||||
impl DatetimeKey {
|
||||
pub fn now() -> Self {
|
||||
Self {
|
||||
datetime: OffsetDateTime::now_utc(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_datetime(datetime: OffsetDateTime) -> Self {
|
||||
Self {
|
||||
datetime: datetime.to_offset(UtcOffset::UTC),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> OffsetDateTime {
|
||||
self.datetime
|
||||
}
|
||||
}
|
||||
|
||||
impl UuidKey {
|
||||
impl<Tag> TaggedKey<UuidKey, Tag> {
|
||||
pub fn generate() -> Self {
|
||||
Self {
|
||||
uuid: Uuid::new_v4(),
|
||||
}
|
||||
UuidKey::generate().tag()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tag> TaggedKey<DatetimeKey, Tag> {
|
||||
pub fn now() -> Self {
|
||||
DatetimeKey::now().tag()
|
||||
}
|
||||
}
|
||||
|
||||
impl EnumKey for ProfileKind {}
|
||||
impl EnumKey for PostKind {}
|
||||
impl EnumKey for ReportState {}
|
||||
|
||||
impl<'k> KeyEncoding<'k, Self> for DatetimeKey {
|
||||
type Error = time::Error;
|
||||
|
||||
const LENGTH: Option<usize> = None;
|
||||
|
||||
fn as_ord_bytes(&'k self) -> Result<std::borrow::Cow<'k, [u8]>, Self::Error> {
|
||||
Ok(std::borrow::Cow::Owned(
|
||||
self.datetime.format(&Rfc3339)?.into_bytes(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'k> Key<'k> for DatetimeKey {
|
||||
fn from_ord_bytes(bytes: &'k [u8]) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
datetime: OffsetDateTime::parse(String::from_utf8_lossy(bytes).as_ref(), &Rfc3339)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'k> KeyEncoding<'k, Self> for UuidKey {
|
||||
type Error = uuid::Error;
|
||||
|
||||
const LENGTH: Option<usize> = Some(16);
|
||||
|
||||
fn as_ord_bytes(&'k self) -> Result<std::borrow::Cow<'k, [u8]>, Self::Error> {
|
||||
Ok(std::borrow::Cow::Borrowed(self.uuid.as_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'k> Key<'k> for UuidKey {
|
||||
fn from_ord_bytes(bytes: &'k [u8]) -> Result<Self, Self::Error> {
|
||||
Ok(UuidKey {
|
||||
uuid: Uuid::from_slice(bytes)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OffsetDateTime> for DatetimeKey {
|
||||
fn from(datetime: OffsetDateTime) -> Self {
|
||||
Self::from_datetime(datetime)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DatetimeKey> for OffsetDateTime {
|
||||
fn from(key: DatetimeKey) -> Self {
|
||||
key.into_inner()
|
||||
}
|
||||
}
|
||||
|
|
184
schema/src/keys/compound_key.rs
Normal file
184
schema/src/keys/compound_key.rs
Normal file
|
@ -0,0 +1,184 @@
|
|||
use super::Impossible;
|
||||
use bonsaidb::core::key::{Key, KeyEncoding};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
|
||||
pub struct CompoundKey<Primary, Secondary> {
|
||||
primary: Primary,
|
||||
secondary: Option<Secondary>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CompoundKeyError<Primary, Secondary> {
|
||||
Primary(Primary),
|
||||
Secondary(Secondary),
|
||||
InvalidFomat,
|
||||
}
|
||||
|
||||
pub type CompoundError<'k, Primary, Secondary> = CompoundKeyError<
|
||||
<Primary as KeyEncoding<'k, Primary>>::Error,
|
||||
<Secondary as KeyEncoding<'k, Secondary>>::Error,
|
||||
>;
|
||||
|
||||
impl<Primary> CompoundKey<Primary, Impossible> {
|
||||
pub fn prefix(primary: Primary) -> Self {
|
||||
Self {
|
||||
primary,
|
||||
secondary: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_secondary<Secondary>(self) -> CompoundKey<Primary, Secondary> {
|
||||
CompoundKey {
|
||||
primary: self.primary,
|
||||
secondary: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Primary, Secondary> CompoundKey<Primary, Secondary> {
|
||||
pub fn new(primary: Primary, secondary: Secondary) -> Self {
|
||||
Self {
|
||||
primary,
|
||||
secondary: Some(secondary),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn primary(&self) -> &Primary {
|
||||
&self.primary
|
||||
}
|
||||
|
||||
pub fn secondary(&self) -> Option<&Secondary> {
|
||||
self.secondary.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Primary, Secondary> std::fmt::Display for CompoundKeyError<Primary, Secondary>
|
||||
where
|
||||
Primary: std::fmt::Display,
|
||||
Secondary: std::fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Primary(primary) => std::fmt::Display::fmt(primary, f),
|
||||
Self::Secondary(secondary) => std::fmt::Display::fmt(secondary, f),
|
||||
Self::InvalidFomat => write!(f, "Compound Key is invalid"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Primary, Secondary> std::error::Error for CompoundKeyError<Primary, Secondary>
|
||||
where
|
||||
Primary: std::error::Error + 'static,
|
||||
Secondary: std::error::Error + 'static,
|
||||
{
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
Self::Primary(primary) => Some(primary),
|
||||
Self::Secondary(secondary) => Some(secondary),
|
||||
Self::InvalidFomat => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'k, Primary, Secondary> KeyEncoding<'k, Self> for CompoundKey<Primary, Secondary>
|
||||
where
|
||||
Primary: Key<'k>,
|
||||
Secondary: Key<'k>,
|
||||
{
|
||||
type Error = CompoundError<'k, Primary, Secondary>;
|
||||
|
||||
const LENGTH: Option<usize> = None;
|
||||
|
||||
fn as_ord_bytes(&'k self) -> Result<std::borrow::Cow<'k, [u8]>, Self::Error> {
|
||||
if let Some(secondary) = &self.secondary {
|
||||
// key version
|
||||
let primary = KeyEncoding::<'k, Primary>::as_ord_bytes(&self.primary)
|
||||
.map_err(CompoundKeyError::Primary)?;
|
||||
let secondary = KeyEncoding::<'k, Secondary>::as_ord_bytes(secondary)
|
||||
.map_err(CompoundKeyError::Secondary)?;
|
||||
|
||||
let mut bytes = Vec::with_capacity(primary.len() + secondary.len() + 24);
|
||||
|
||||
bytes.extend_from_slice(&primary);
|
||||
|
||||
bytes.extend([0u8; 4]);
|
||||
|
||||
bytes.extend_from_slice(&secondary);
|
||||
|
||||
bytes.extend((primary.len() as u64).to_be_bytes());
|
||||
bytes.extend((secondary.len() as u64).to_be_bytes());
|
||||
|
||||
bytes.extend([0, 0, 0, 1]);
|
||||
|
||||
Ok(std::borrow::Cow::Owned(bytes))
|
||||
} else {
|
||||
// prefix version
|
||||
let primary = KeyEncoding::<'k, Primary>::as_ord_bytes(&self.primary)
|
||||
.map_err(CompoundKeyError::Primary)?;
|
||||
|
||||
let mut bytes = Vec::with_capacity(primary.len() + 4);
|
||||
bytes.extend_from_slice(&primary);
|
||||
bytes.extend([0u8; 4]);
|
||||
|
||||
Ok(std::borrow::Cow::Owned(bytes))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'k, Primary, Secondary> Key<'k> for CompoundKey<Primary, Secondary>
|
||||
where
|
||||
Primary: Key<'k>,
|
||||
Secondary: Key<'k>,
|
||||
{
|
||||
fn from_ord_bytes(bytes: &'k [u8]) -> Result<Self, Self::Error> {
|
||||
if bytes.len() < 4 {
|
||||
return Err(CompoundKeyError::InvalidFomat);
|
||||
}
|
||||
|
||||
let flags_start = bytes.len() - 4;
|
||||
|
||||
let flags = &bytes[flags_start..];
|
||||
if flags == [0u8; 4] {
|
||||
// prefix
|
||||
let primary = Primary::from_ord_bytes(&bytes[..flags_start])
|
||||
.map_err(CompoundKeyError::Primary)?;
|
||||
|
||||
return Ok(CompoundKey {
|
||||
primary,
|
||||
secondary: None,
|
||||
});
|
||||
}
|
||||
|
||||
if bytes.len() < 24 {
|
||||
return Err(CompoundKeyError::InvalidFomat);
|
||||
}
|
||||
|
||||
let secondary_len_start = flags_start - 8;
|
||||
let primary_len_start = secondary_len_start - 8;
|
||||
|
||||
let secondary_len_bytes = TryFrom::try_from(&bytes[secondary_len_start..])
|
||||
.expect("We've already length-checked this");
|
||||
let primary_len_bytes = TryFrom::try_from(&bytes[primary_len_start..secondary_len_start])
|
||||
.expect("We've already length-checked this");
|
||||
|
||||
let secondary_len = u64::from_be_bytes(secondary_len_bytes) as usize;
|
||||
let primary_len = u64::from_be_bytes(primary_len_bytes) as usize;
|
||||
|
||||
let primary_start = 0;
|
||||
let primary_end = primary_start + primary_len;
|
||||
|
||||
let secondary_start = primary_end + 4;
|
||||
let secondary_end = secondary_start + secondary_len;
|
||||
|
||||
let primary = Primary::from_ord_bytes(&bytes[primary_start..primary_end])
|
||||
.map_err(CompoundKeyError::Primary)?;
|
||||
let secondary = Secondary::from_ord_bytes(&bytes[secondary_start..secondary_end])
|
||||
.map_err(CompoundKeyError::Secondary)?;
|
||||
|
||||
Ok(CompoundKey {
|
||||
primary,
|
||||
secondary: Some(secondary),
|
||||
})
|
||||
}
|
||||
}
|
60
schema/src/keys/datetime_key.rs
Normal file
60
schema/src/keys/datetime_key.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
use bonsaidb::core::key::{Key, KeyEncoding};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::{format_description::well_known::Rfc3339, OffsetDateTime, UtcOffset};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct DatetimeKey {
|
||||
#[serde(with = "time::serde::rfc3339")]
|
||||
datetime: OffsetDateTime,
|
||||
}
|
||||
|
||||
impl DatetimeKey {
|
||||
pub fn now() -> Self {
|
||||
Self {
|
||||
datetime: OffsetDateTime::now_utc(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_datetime(datetime: OffsetDateTime) -> Self {
|
||||
Self {
|
||||
datetime: datetime.to_offset(UtcOffset::UTC),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> OffsetDateTime {
|
||||
self.datetime
|
||||
}
|
||||
}
|
||||
|
||||
impl<'k> KeyEncoding<'k, Self> for DatetimeKey {
|
||||
type Error = time::Error;
|
||||
|
||||
const LENGTH: Option<usize> = None;
|
||||
|
||||
fn as_ord_bytes(&'k self) -> Result<std::borrow::Cow<'k, [u8]>, Self::Error> {
|
||||
Ok(std::borrow::Cow::Owned(
|
||||
self.datetime.format(&Rfc3339)?.into_bytes(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'k> Key<'k> for DatetimeKey {
|
||||
fn from_ord_bytes(bytes: &'k [u8]) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
datetime: OffsetDateTime::parse(String::from_utf8_lossy(bytes).as_ref(), &Rfc3339)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OffsetDateTime> for DatetimeKey {
|
||||
fn from(datetime: OffsetDateTime) -> Self {
|
||||
Self::from_datetime(datetime)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DatetimeKey> for OffsetDateTime {
|
||||
fn from(key: DatetimeKey) -> Self {
|
||||
key.into_inner()
|
||||
}
|
||||
}
|
32
schema/src/keys/impossible.rs
Normal file
32
schema/src/keys/impossible.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
use bonsaidb::core::key::{Key, KeyEncoding};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum Impossible {}
|
||||
|
||||
impl std::fmt::Display for Impossible {
|
||||
fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match *self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Impossible {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match *self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'k> KeyEncoding<'k, Self> for Impossible {
|
||||
type Error = Impossible;
|
||||
|
||||
const LENGTH: Option<usize> = None;
|
||||
|
||||
fn as_ord_bytes(&'k self) -> Result<std::borrow::Cow<'k, [u8]>, Self::Error> {
|
||||
match *self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'k> Key<'k> for Impossible {
|
||||
fn from_ord_bytes(_bytes: &'k [u8]) -> Result<Self, Self::Error> {
|
||||
panic!("Should never construct an impossible type")
|
||||
}
|
||||
}
|
167
schema/src/keys/tagged_key.rs
Normal file
167
schema/src/keys/tagged_key.rs
Normal file
|
@ -0,0 +1,167 @@
|
|||
use bonsaidb::core::key::{Key, KeyEncoding};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub struct TaggedKey<Inner, Tag> {
|
||||
inner: Inner,
|
||||
tag: PhantomData<fn() -> Tag>,
|
||||
}
|
||||
|
||||
impl<Inner, Tag> TaggedKey<Inner, Tag> {
|
||||
pub fn new(inner: Inner) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
tag: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Inner, Tag> Clone for TaggedKey<Inner, Tag>
|
||||
where
|
||||
Inner: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: self.inner.clone(),
|
||||
tag: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Inner, Tag> Copy for TaggedKey<Inner, Tag> where Inner: Copy {}
|
||||
|
||||
impl<Inner, Tag> std::fmt::Debug for TaggedKey<Inner, Tag>
|
||||
where
|
||||
Inner: std::fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Debug::fmt(&self.inner, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Inner, Tag> std::fmt::Display for TaggedKey<Inner, Tag>
|
||||
where
|
||||
Inner: std::fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(&self.inner, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Inner, Tag> PartialEq for TaggedKey<Inner, Tag>
|
||||
where
|
||||
Inner: PartialEq,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.inner == other.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<Inner, Tag> Eq for TaggedKey<Inner, Tag> where Inner: Eq {}
|
||||
|
||||
impl<Inner, Tag> PartialOrd for TaggedKey<Inner, Tag>
|
||||
where
|
||||
Inner: PartialOrd,
|
||||
{
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
self.inner.partial_cmp(&other.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Inner, Tag> Ord for TaggedKey<Inner, Tag>
|
||||
where
|
||||
Inner: Ord,
|
||||
{
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.inner.cmp(&other.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Inner, Tag> std::hash::Hash for TaggedKey<Inner, Tag>
|
||||
where
|
||||
Inner: std::hash::Hash,
|
||||
{
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.inner.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, Inner, Tag> Deserialize<'de> for TaggedKey<Inner, Tag>
|
||||
where
|
||||
Inner: Deserialize<'de>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let inner = Inner::deserialize(deserializer)?;
|
||||
|
||||
Ok(Self {
|
||||
inner,
|
||||
tag: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Inner, Tag> Serialize for TaggedKey<Inner, Tag>
|
||||
where
|
||||
Inner: Serialize,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::ser::Serializer,
|
||||
{
|
||||
self.inner.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'k, Inner, Tag> KeyEncoding<'k, Self> for TaggedKey<Inner, Tag>
|
||||
where
|
||||
Inner: Key<'k>,
|
||||
{
|
||||
type Error = Inner::Error;
|
||||
|
||||
const LENGTH: Option<usize> = Inner::LENGTH;
|
||||
|
||||
fn as_ord_bytes(&'k self) -> Result<std::borrow::Cow<'k, [u8]>, Self::Error> {
|
||||
self.inner.as_ord_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'k, Inner, Tag> Key<'k> for TaggedKey<Inner, Tag>
|
||||
where
|
||||
Inner: Key<'k>,
|
||||
{
|
||||
fn from_ord_bytes(bytes: &'k [u8]) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
inner: Inner::from_ord_bytes(bytes)?,
|
||||
tag: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn first_value() -> Result<Self, bonsaidb::core::key::NextValueError> {
|
||||
Ok(Self {
|
||||
inner: Inner::first_value()?,
|
||||
tag: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn next_value(&self) -> Result<Self, bonsaidb::core::key::NextValueError> {
|
||||
Ok(Self {
|
||||
inner: self.inner.next_value()?,
|
||||
tag: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Inner, Tag> AsRef<Inner> for TaggedKey<Inner, Tag> {
|
||||
fn as_ref(&self) -> &Inner {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<Inner, Tag> AsMut<Inner> for TaggedKey<Inner, Tag> {
|
||||
fn as_mut(&mut self) -> &mut Inner {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
35
schema/src/keys/uuid_key.rs
Normal file
35
schema/src/keys/uuid_key.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
use bonsaidb::core::key::{Key, KeyEncoding};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct UuidKey {
|
||||
uuid: Uuid,
|
||||
}
|
||||
|
||||
impl UuidKey {
|
||||
pub fn generate() -> Self {
|
||||
Self {
|
||||
uuid: Uuid::new_v4(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'k> KeyEncoding<'k, Self> for UuidKey {
|
||||
type Error = uuid::Error;
|
||||
|
||||
const LENGTH: Option<usize> = Some(16);
|
||||
|
||||
fn as_ord_bytes(&'k self) -> Result<std::borrow::Cow<'k, [u8]>, Self::Error> {
|
||||
Ok(std::borrow::Cow::Borrowed(self.uuid.as_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'k> Key<'k> for UuidKey {
|
||||
fn from_ord_bytes(bytes: &'k [u8]) -> Result<Self, Self::Error> {
|
||||
Ok(UuidKey {
|
||||
uuid: Uuid::from_slice(bytes)?,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
pub mod keys;
|
||||
pub mod post;
|
||||
|
||||
use bonsaidb::core::{
|
||||
document::{CollectionDocument, Emit},
|
||||
|
@ -7,7 +8,8 @@ use bonsaidb::core::{
|
|||
Collection, ReduceResult, Schema, View, ViewMapResult,
|
||||
},
|
||||
};
|
||||
use keys::{DatetimeKey, PostKind, ProfileKind, ReportState, UuidKey};
|
||||
use keys::{Created, DatetimeKey, KeyExt, ProfileKind, ReportState, TaggedKey, Updated, UuidKey};
|
||||
use post::Post;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Schema)]
|
||||
|
@ -28,24 +30,28 @@ pub struct SiteSchema;
|
|||
#[collection(
|
||||
name = "accounts",
|
||||
views = [AccountsByUsername, AccountsByEmail],
|
||||
primary_key = UuidKey,
|
||||
primary_key = TaggedKey<UuidKey, Account>,
|
||||
natural_id = |account: &Account| Some(account.id)
|
||||
)]
|
||||
pub struct Account {
|
||||
id: UuidKey,
|
||||
id: TaggedKey<UuidKey, Self>,
|
||||
|
||||
username: String,
|
||||
emails: Vec<String>,
|
||||
|
||||
created_at: TaggedKey<DatetimeKey, Created>,
|
||||
updated_at: TaggedKey<DatetimeKey, Updated>,
|
||||
}
|
||||
|
||||
#[derive(Collection, Debug, Deserialize, Serialize)]
|
||||
#[collection(
|
||||
name = "profiles",
|
||||
views = [ProfilesByHandle, RemoteProfilesByDomain, LocalProfilesById, ProfilesByCollaborator, ProfilesByKind],
|
||||
primary_key = UuidKey,
|
||||
primary_key = TaggedKey<UuidKey, Profile>,
|
||||
natural_id = |profile: &Profile| Some(profile.id)
|
||||
)]
|
||||
pub struct Profile {
|
||||
id: UuidKey,
|
||||
id: TaggedKey<UuidKey, Self>,
|
||||
|
||||
kind: ProfileKind,
|
||||
|
||||
|
@ -56,13 +62,11 @@ pub struct Profile {
|
|||
name: Option<String>,
|
||||
description: Option<String>,
|
||||
|
||||
// Account Key
|
||||
local_account: Option<UuidKey>,
|
||||
// Profile Keys
|
||||
collaborators: Vec<UuidKey>,
|
||||
local_account: Option<TaggedKey<UuidKey, Account>>,
|
||||
collaborators: Vec<TaggedKey<UuidKey, Profile>>,
|
||||
|
||||
created_at: DatetimeKey,
|
||||
updated_at: DatetimeKey,
|
||||
created_at: TaggedKey<DatetimeKey, Created>,
|
||||
updated_at: TaggedKey<DatetimeKey, Updated>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
|
@ -81,108 +85,55 @@ pub enum MediaState {
|
|||
#[collection(
|
||||
name = "media",
|
||||
views = [],
|
||||
primary_key = UuidKey,
|
||||
primary_key = TaggedKey<UuidKey, Media>,
|
||||
natural_id = |media: &Media| Some(media.id)
|
||||
)]
|
||||
pub struct Media {
|
||||
id: UuidKey,
|
||||
id: TaggedKey<UuidKey, Self>,
|
||||
|
||||
state: MediaState,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum DeletedBy {
|
||||
Author,
|
||||
PageOwner {
|
||||
// Profile key
|
||||
profile_id: UuidKey,
|
||||
},
|
||||
Moderator {
|
||||
// Account key
|
||||
account_id: UuidKey,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct History {
|
||||
content: String,
|
||||
date: DatetimeKey,
|
||||
}
|
||||
|
||||
#[derive(Collection, Debug, Deserialize, Serialize)]
|
||||
#[collection(
|
||||
name = "post",
|
||||
views = [PostsByKind, PostsByAuthor, PostsByTag, PostsByMention, PostsByParent],
|
||||
primary_key = UuidKey,
|
||||
natural_id = |post: &Post| Some(post.id)
|
||||
)]
|
||||
pub struct Post {
|
||||
id: UuidKey,
|
||||
|
||||
deleted_by: Option<DeletedBy>,
|
||||
|
||||
title: String,
|
||||
title_history: Vec<History>,
|
||||
|
||||
description: Option<String>,
|
||||
description_history: Vec<History>,
|
||||
|
||||
// Media key
|
||||
media: Vec<UuidKey>,
|
||||
|
||||
// Tag key
|
||||
tags: Vec<UuidKey>,
|
||||
|
||||
// Profile key
|
||||
mentions: Vec<UuidKey>,
|
||||
|
||||
// Post key
|
||||
reply_to: Option<UuidKey>,
|
||||
|
||||
// Profile key
|
||||
author: UuidKey,
|
||||
|
||||
created_at: DatetimeKey,
|
||||
updated_at: DatetimeKey,
|
||||
created_at: TaggedKey<DatetimeKey, Created>,
|
||||
updated_at: TaggedKey<DatetimeKey, Updated>,
|
||||
}
|
||||
|
||||
#[derive(Collection, Debug, Deserialize, Serialize)]
|
||||
#[collection(
|
||||
name = "tags",
|
||||
views = [TagsByName],
|
||||
primary_key = UuidKey,
|
||||
primary_key = TaggedKey<UuidKey, Tag>,
|
||||
natural_id = |tag: &Tag| Some(tag.id)
|
||||
)]
|
||||
pub struct Tag {
|
||||
id: UuidKey,
|
||||
id: TaggedKey<UuidKey, Self>,
|
||||
|
||||
name: String,
|
||||
|
||||
created_at: TaggedKey<DatetimeKey, Created>,
|
||||
updated_at: TaggedKey<DatetimeKey, Updated>,
|
||||
}
|
||||
|
||||
#[derive(Collection, Debug, Deserialize, Serialize)]
|
||||
#[collection(
|
||||
name = "reports",
|
||||
views = [ReportsByPost, ReportsByState, ReportsByAccount],
|
||||
primary_key = UuidKey,
|
||||
primary_key = TaggedKey<UuidKey, Report>,
|
||||
natural_id = |report: &Report| Some(report.id)
|
||||
)]
|
||||
pub struct Report {
|
||||
id: UuidKey,
|
||||
id: TaggedKey<UuidKey, Self>,
|
||||
|
||||
message: Option<String>,
|
||||
moderation_note: Option<String>,
|
||||
|
||||
// Post key
|
||||
post: UuidKey,
|
||||
post: TaggedKey<UuidKey, Post>,
|
||||
|
||||
state: ReportState,
|
||||
|
||||
// Account Key
|
||||
handled_by: Option<UuidKey>,
|
||||
handled_by: Option<TaggedKey<UuidKey, Account>>,
|
||||
|
||||
created_at: DatetimeKey,
|
||||
updated_at: DatetimeKey,
|
||||
created_at: TaggedKey<DatetimeKey, Created>,
|
||||
updated_at: TaggedKey<DatetimeKey, Updated>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
|
@ -202,43 +153,23 @@ pub struct ProfilesByHandle;
|
|||
pub struct RemoteProfilesByDomain;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
#[view(collection = Profile, key = Option<UuidKey>, value = usize, name = "by-local-id")]
|
||||
#[view(collection = Profile, key = Option<TaggedKey<UuidKey, Account>>, value = usize, name = "by-local-id")]
|
||||
pub struct LocalProfilesById;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
#[view(collection = Profile, key = UuidKey, value = usize, name = "by-collaborator")]
|
||||
#[view(collection = Profile, key = TaggedKey<UuidKey, Profile>, value = usize, name = "by-collaborator")]
|
||||
pub struct ProfilesByCollaborator;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
#[view(collection = Profile, key = ProfileKind, value = usize, name = "by-kind")]
|
||||
pub struct ProfilesByKind;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
#[view(collection = Post, key = PostKind, value = usize, name = "by-kind")]
|
||||
pub struct PostsByKind;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
#[view(collection = Post, key = UuidKey, value = usize, name = "by-author")]
|
||||
pub struct PostsByAuthor;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
#[view(collection = Post, key = UuidKey, value = usize, name = "by-tag")]
|
||||
pub struct PostsByTag;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
#[view(collection = Post, key = UuidKey, value = usize, name = "by-mention")]
|
||||
pub struct PostsByMention;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
#[view(collection = Post, key = Option<UuidKey>, value = usize, name = "by-parent")]
|
||||
pub struct PostsByParent;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
#[view(collection = Tag, key = String, value = usize, name = "by-name")]
|
||||
pub struct TagsByName;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
#[view(collection = Report, key = UuidKey, value = usize, name = "by-name")]
|
||||
#[view(collection = Report, key = TaggedKey<UuidKey, Post>, value = usize, name = "by-name")]
|
||||
pub struct ReportsByPost;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
|
@ -246,7 +177,7 @@ pub struct ReportsByPost;
|
|||
pub struct ReportsByState;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
#[view(collection = Report, key = Option<UuidKey>, value = usize, name = "by-account")]
|
||||
#[view(collection = Report, key = Option<TaggedKey<UuidKey, Account>>, value = usize, name = "by-account")]
|
||||
pub struct ReportsByAccount;
|
||||
|
||||
impl CollectionViewSchema for AccountsByUsername {
|
||||
|
@ -278,7 +209,7 @@ impl CollectionViewSchema for AccountsByEmail {
|
|||
document: CollectionDocument<<Self::View as View>::Collection>,
|
||||
) -> ViewMapResult<Self::View> {
|
||||
if document.contents.emails.is_empty() {
|
||||
return Ok(Mappings::Simple(None));
|
||||
return Ok(Mappings::none());
|
||||
}
|
||||
|
||||
let mut mappings = Vec::new();
|
||||
|
@ -309,7 +240,7 @@ impl CollectionViewSchema for ProfilesByHandle {
|
|||
let mut mappings = Vec::new();
|
||||
|
||||
if document.contents.handle.is_empty() {
|
||||
return Ok(Mappings::Simple(None));
|
||||
return Ok(Mappings::none());
|
||||
}
|
||||
|
||||
const MAX_SEARCH_LEN: usize = 16;
|
||||
|
@ -396,7 +327,7 @@ impl CollectionViewSchema for ProfilesByCollaborator {
|
|||
document: CollectionDocument<<Self::View as View>::Collection>,
|
||||
) -> ViewMapResult<Self::View> {
|
||||
if document.contents.collaborators.is_empty() {
|
||||
return Ok(Mappings::Simple(None));
|
||||
return Ok(Mappings::none());
|
||||
}
|
||||
|
||||
let mut mappings = Vec::new();
|
||||
|
@ -437,131 +368,6 @@ impl CollectionViewSchema for ProfilesByKind {
|
|||
}
|
||||
}
|
||||
|
||||
impl CollectionViewSchema for PostsByKind {
|
||||
type View = Self;
|
||||
|
||||
fn map(
|
||||
&self,
|
||||
document: CollectionDocument<<Self as View>::Collection>,
|
||||
) -> ViewMapResult<Self::View> {
|
||||
let kind = if !document.contents.media.is_empty() {
|
||||
PostKind::Media
|
||||
} else if document.contents.reply_to.is_some() {
|
||||
PostKind::Comment
|
||||
} else {
|
||||
PostKind::Text
|
||||
};
|
||||
|
||||
document.header.emit_key_and_value(kind, 1)
|
||||
}
|
||||
|
||||
fn reduce(
|
||||
&self,
|
||||
mappings: &[bonsaidb::core::schema::ViewMappedValue<Self::View>],
|
||||
_rereduce: bool,
|
||||
) -> ReduceResult<Self::View> {
|
||||
Ok(mappings.iter().map(|m| m.value).sum())
|
||||
}
|
||||
}
|
||||
|
||||
impl CollectionViewSchema for PostsByAuthor {
|
||||
type View = Self;
|
||||
|
||||
fn map(
|
||||
&self,
|
||||
document: CollectionDocument<<Self::View as View>::Collection>,
|
||||
) -> ViewMapResult<Self::View> {
|
||||
document
|
||||
.header
|
||||
.emit_key_and_value(document.contents.author, 1)
|
||||
}
|
||||
|
||||
fn reduce(
|
||||
&self,
|
||||
mappings: &[bonsaidb::core::schema::ViewMappedValue<Self::View>],
|
||||
_rereduce: bool,
|
||||
) -> ReduceResult<Self::View> {
|
||||
Ok(mappings.iter().map(|m| m.value).sum())
|
||||
}
|
||||
}
|
||||
|
||||
impl CollectionViewSchema for PostsByTag {
|
||||
type View = Self;
|
||||
|
||||
fn map(
|
||||
&self,
|
||||
document: CollectionDocument<<Self::View as View>::Collection>,
|
||||
) -> ViewMapResult<Self::View> {
|
||||
if document.contents.tags.is_empty() {
|
||||
return Ok(Mappings::Simple(None));
|
||||
}
|
||||
|
||||
let mut mappings = Vec::new();
|
||||
for tag in document.contents.tags {
|
||||
mappings.extend(document.header.emit_key_and_value(tag, 1)?);
|
||||
}
|
||||
|
||||
Ok(Mappings::List(mappings))
|
||||
}
|
||||
|
||||
fn reduce(
|
||||
&self,
|
||||
mappings: &[bonsaidb::core::schema::ViewMappedValue<Self::View>],
|
||||
_rereduce: bool,
|
||||
) -> ReduceResult<Self::View> {
|
||||
Ok(mappings.iter().map(|m| m.value).sum())
|
||||
}
|
||||
}
|
||||
|
||||
impl CollectionViewSchema for PostsByMention {
|
||||
type View = Self;
|
||||
|
||||
fn map(
|
||||
&self,
|
||||
document: CollectionDocument<<Self::View as View>::Collection>,
|
||||
) -> ViewMapResult<Self::View> {
|
||||
if document.contents.mentions.is_empty() {
|
||||
return Ok(Mappings::Simple(None));
|
||||
}
|
||||
|
||||
let mut mappings = Vec::new();
|
||||
for mention in document.contents.mentions {
|
||||
mappings.extend(document.header.emit_key_and_value(mention, 1)?);
|
||||
}
|
||||
|
||||
Ok(Mappings::List(mappings))
|
||||
}
|
||||
|
||||
fn reduce(
|
||||
&self,
|
||||
mappings: &[bonsaidb::core::schema::ViewMappedValue<Self::View>],
|
||||
_rereduce: bool,
|
||||
) -> ReduceResult<Self::View> {
|
||||
Ok(mappings.iter().map(|m| m.value).sum())
|
||||
}
|
||||
}
|
||||
|
||||
impl CollectionViewSchema for PostsByParent {
|
||||
type View = Self;
|
||||
|
||||
fn map(
|
||||
&self,
|
||||
document: CollectionDocument<<Self::View as View>::Collection>,
|
||||
) -> ViewMapResult<Self::View> {
|
||||
document
|
||||
.header
|
||||
.emit_key_and_value(document.contents.reply_to, 1)
|
||||
}
|
||||
|
||||
fn reduce(
|
||||
&self,
|
||||
mappings: &[bonsaidb::core::schema::ViewMappedValue<Self::View>],
|
||||
_rereduce: bool,
|
||||
) -> ReduceResult<Self::View> {
|
||||
Ok(mappings.iter().map(|m| m.value).sum())
|
||||
}
|
||||
}
|
||||
|
||||
impl CollectionViewSchema for TagsByName {
|
||||
type View = Self;
|
||||
|
||||
|
@ -649,8 +455,10 @@ impl CollectionViewSchema for ReportsByAccount {
|
|||
impl Tag {
|
||||
pub fn new(name: String) -> Self {
|
||||
Self {
|
||||
id: UuidKey::generate(),
|
||||
id: UuidKey::generate().tag(),
|
||||
name,
|
||||
created_at: DatetimeKey::now().tag(),
|
||||
updated_at: DatetimeKey::now().tag(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
400
schema/src/post.rs
Normal file
400
schema/src/post.rs
Normal file
|
@ -0,0 +1,400 @@
|
|||
use super::{
|
||||
keys::{
|
||||
CompoundKey, Created, DatetimeKey, Deleted, KeyExt, PostKind, Published, TaggedKey,
|
||||
Updated, UuidKey,
|
||||
},
|
||||
Account, Media, Profile, Tag,
|
||||
};
|
||||
use bonsaidb::core::{
|
||||
document::{CollectionDocument, Emit},
|
||||
schema::{
|
||||
view::{map::Mappings, CollectionViewSchema},
|
||||
Collection, ReduceResult, View, ViewMapResult,
|
||||
},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum DeletedBy {
|
||||
Author,
|
||||
PageOwner {
|
||||
profile_id: TaggedKey<UuidKey, Profile>,
|
||||
},
|
||||
Moderator {
|
||||
account_id: TaggedKey<UuidKey, Account>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Collection, Debug, Deserialize, Serialize)]
|
||||
#[collection(
|
||||
name = "post",
|
||||
views = [PostsByKind, PublishedPostsByKind, PostsByAuthor, PublishedPostsByAuthor, UnpublishedPostsByAuthor, UnpublishedPostsByAuthorAndKind, PostsByTag, PostsByMention, PostsByParent],
|
||||
primary_key = TaggedKey<UuidKey, Post>,
|
||||
natural_id = |post: &Post| Some(post.id)
|
||||
)]
|
||||
pub struct Post {
|
||||
id: TaggedKey<UuidKey, Self>,
|
||||
|
||||
deleted_by: Option<DeletedBy>,
|
||||
|
||||
title: String,
|
||||
description: Option<String>,
|
||||
|
||||
media: Vec<TaggedKey<UuidKey, Media>>,
|
||||
tags: Vec<TaggedKey<UuidKey, Tag>>,
|
||||
mentions: Vec<TaggedKey<UuidKey, Profile>>,
|
||||
reply_to: Option<TaggedKey<UuidKey, Self>>,
|
||||
author: TaggedKey<UuidKey, Profile>,
|
||||
|
||||
created_at: TaggedKey<DatetimeKey, Created>,
|
||||
updated_at: TaggedKey<DatetimeKey, Updated>,
|
||||
published_at: Option<TaggedKey<DatetimeKey, Published>>,
|
||||
deleted_at: Option<TaggedKey<DatetimeKey, Deleted>>,
|
||||
}
|
||||
|
||||
pub type PostKindPublishedKey = CompoundKey<PostKind, TaggedKey<DatetimeKey, Published>>;
|
||||
|
||||
pub type AuthorPublishedKey =
|
||||
CompoundKey<TaggedKey<UuidKey, Profile>, TaggedKey<DatetimeKey, Published>>;
|
||||
|
||||
pub type AuthorPostKindPublishedKey = CompoundKey<
|
||||
CompoundKey<TaggedKey<UuidKey, Profile>, PostKind>,
|
||||
TaggedKey<DatetimeKey, Published>,
|
||||
>;
|
||||
|
||||
pub type AuthorUnpublishedKey =
|
||||
CompoundKey<TaggedKey<UuidKey, Profile>, TaggedKey<DatetimeKey, Created>>;
|
||||
|
||||
pub type AuthorPostKindUnpublishedKey = CompoundKey<
|
||||
CompoundKey<TaggedKey<UuidKey, Profile>, PostKind>,
|
||||
TaggedKey<DatetimeKey, Created>,
|
||||
>;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
#[view(collection = Post, key = PostKind, value = usize, name = "by-kind")]
|
||||
pub struct PostsByKind;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
#[view(collection = Post, key = PostKindPublishedKey, value = usize, name = "by-kind-and-published")]
|
||||
pub struct PublishedPostsByKind;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
#[view(collection = Post, key = TaggedKey<UuidKey, Profile>, value = usize, name = "by-author")]
|
||||
pub struct PostsByAuthor;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
#[view(collection = Post, key = AuthorPublishedKey, value = usize, name = "by-author-and-published")]
|
||||
pub struct PublishedPostsByAuthor;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
#[view(collection = Post, key = AuthorPostKindPublishedKey, value = usize, name = "by-author-and-published")]
|
||||
pub struct PublishedPostsByAuthorAndKind;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
#[view(collection = Post, key = AuthorUnpublishedKey, value = usize, name = "by-author-and-unpublished")]
|
||||
pub struct UnpublishedPostsByAuthor;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
#[view(collection = Post, key = AuthorPostKindUnpublishedKey, value = usize, name = "by-author-and-unpublished")]
|
||||
pub struct UnpublishedPostsByAuthorAndKind;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
#[view(collection = Post, key = TaggedKey<UuidKey, Tag>, value = usize, name = "by-tag")]
|
||||
pub struct PostsByTag;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
#[view(collection = Post, key = TaggedKey<UuidKey, Profile>, value = usize, name = "by-mention")]
|
||||
pub struct PostsByMention;
|
||||
|
||||
#[derive(Debug, Clone, View)]
|
||||
#[view(collection = Post, key = Option<TaggedKey<UuidKey, Post>>, value = usize, name = "by-parent")]
|
||||
pub struct PostsByParent;
|
||||
|
||||
impl CollectionViewSchema for PostsByKind {
|
||||
type View = Self;
|
||||
|
||||
fn map(
|
||||
&self,
|
||||
document: CollectionDocument<<Self as View>::Collection>,
|
||||
) -> ViewMapResult<Self::View> {
|
||||
let kind = if !document.contents.media.is_empty() {
|
||||
PostKind::Media
|
||||
} else if document.contents.reply_to.is_some() {
|
||||
PostKind::Comment
|
||||
} else {
|
||||
PostKind::Text
|
||||
};
|
||||
|
||||
document.header.emit_key_and_value(kind, 1)
|
||||
}
|
||||
|
||||
fn reduce(
|
||||
&self,
|
||||
mappings: &[bonsaidb::core::schema::ViewMappedValue<Self::View>],
|
||||
_rereduce: bool,
|
||||
) -> ReduceResult<Self::View> {
|
||||
Ok(mappings.iter().map(|m| m.value).sum())
|
||||
}
|
||||
}
|
||||
|
||||
impl CollectionViewSchema for PublishedPostsByKind {
|
||||
type View = Self;
|
||||
|
||||
fn map(
|
||||
&self,
|
||||
document: CollectionDocument<<Self as View>::Collection>,
|
||||
) -> ViewMapResult<Self::View> {
|
||||
if let Some(published_at) = document.contents.published_at {
|
||||
let kind = if !document.contents.media.is_empty() {
|
||||
PostKind::Media
|
||||
} else if document.contents.reply_to.is_some() {
|
||||
PostKind::Comment
|
||||
} else {
|
||||
PostKind::Text
|
||||
};
|
||||
|
||||
document
|
||||
.header
|
||||
.emit_key_and_value(kind.compound(published_at), 1)
|
||||
} else {
|
||||
Ok(Mappings::none())
|
||||
}
|
||||
}
|
||||
|
||||
fn reduce(
|
||||
&self,
|
||||
mappings: &[bonsaidb::core::schema::ViewMappedValue<Self::View>],
|
||||
_rereduce: bool,
|
||||
) -> ReduceResult<Self::View> {
|
||||
Ok(mappings.iter().map(|m| m.value).sum())
|
||||
}
|
||||
}
|
||||
|
||||
impl CollectionViewSchema for PostsByAuthor {
|
||||
type View = Self;
|
||||
|
||||
fn map(
|
||||
&self,
|
||||
document: CollectionDocument<<Self::View as View>::Collection>,
|
||||
) -> ViewMapResult<Self::View> {
|
||||
document
|
||||
.header
|
||||
.emit_key_and_value(document.contents.author, 1)
|
||||
}
|
||||
|
||||
fn reduce(
|
||||
&self,
|
||||
mappings: &[bonsaidb::core::schema::ViewMappedValue<Self::View>],
|
||||
_rereduce: bool,
|
||||
) -> ReduceResult<Self::View> {
|
||||
Ok(mappings.iter().map(|m| m.value).sum())
|
||||
}
|
||||
}
|
||||
|
||||
impl CollectionViewSchema for PublishedPostsByAuthor {
|
||||
type View = Self;
|
||||
|
||||
fn map(
|
||||
&self,
|
||||
document: CollectionDocument<<Self::View as View>::Collection>,
|
||||
) -> ViewMapResult<Self::View> {
|
||||
if let Some(published_at) = document.contents.published_at {
|
||||
document
|
||||
.header
|
||||
.emit_key_and_value(document.contents.author.compound(published_at), 1)
|
||||
} else {
|
||||
Ok(Mappings::none())
|
||||
}
|
||||
}
|
||||
|
||||
fn reduce(
|
||||
&self,
|
||||
mappings: &[bonsaidb::core::schema::ViewMappedValue<Self::View>],
|
||||
_rereduce: bool,
|
||||
) -> ReduceResult<Self::View> {
|
||||
Ok(mappings.iter().map(|m| m.value).sum())
|
||||
}
|
||||
}
|
||||
|
||||
impl CollectionViewSchema for PublishedPostsByAuthorAndKind {
|
||||
type View = Self;
|
||||
|
||||
fn map(
|
||||
&self,
|
||||
document: CollectionDocument<<Self::View as View>::Collection>,
|
||||
) -> ViewMapResult<Self::View> {
|
||||
if let Some(published_at) = document.contents.published_at {
|
||||
let kind = if !document.contents.media.is_empty() {
|
||||
PostKind::Media
|
||||
} else if document.contents.reply_to.is_some() {
|
||||
PostKind::Comment
|
||||
} else {
|
||||
PostKind::Text
|
||||
};
|
||||
|
||||
document.header.emit_key_and_value(
|
||||
document
|
||||
.contents
|
||||
.author
|
||||
.compound(kind)
|
||||
.compound(published_at),
|
||||
1,
|
||||
)
|
||||
} else {
|
||||
Ok(Mappings::none())
|
||||
}
|
||||
}
|
||||
|
||||
fn reduce(
|
||||
&self,
|
||||
mappings: &[bonsaidb::core::schema::ViewMappedValue<Self::View>],
|
||||
_rereduce: bool,
|
||||
) -> ReduceResult<Self::View> {
|
||||
Ok(mappings.iter().map(|m| m.value).sum())
|
||||
}
|
||||
}
|
||||
|
||||
impl CollectionViewSchema for UnpublishedPostsByAuthor {
|
||||
type View = Self;
|
||||
|
||||
fn map(
|
||||
&self,
|
||||
document: CollectionDocument<<Self::View as View>::Collection>,
|
||||
) -> ViewMapResult<Self::View> {
|
||||
if document.contents.published_at.is_some() {
|
||||
Ok(Mappings::none())
|
||||
} else {
|
||||
document.header.emit_key_and_value(
|
||||
document
|
||||
.contents
|
||||
.author
|
||||
.compound(document.contents.created_at),
|
||||
1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn reduce(
|
||||
&self,
|
||||
mappings: &[bonsaidb::core::schema::ViewMappedValue<Self::View>],
|
||||
_rereduce: bool,
|
||||
) -> ReduceResult<Self::View> {
|
||||
Ok(mappings.iter().map(|m| m.value).sum())
|
||||
}
|
||||
}
|
||||
|
||||
impl CollectionViewSchema for UnpublishedPostsByAuthorAndKind {
|
||||
type View = Self;
|
||||
|
||||
fn map(
|
||||
&self,
|
||||
document: CollectionDocument<<Self::View as View>::Collection>,
|
||||
) -> ViewMapResult<Self::View> {
|
||||
if document.contents.published_at.is_some() {
|
||||
Ok(Mappings::none())
|
||||
} else {
|
||||
let kind = if !document.contents.media.is_empty() {
|
||||
PostKind::Media
|
||||
} else if document.contents.reply_to.is_some() {
|
||||
PostKind::Comment
|
||||
} else {
|
||||
PostKind::Text
|
||||
};
|
||||
|
||||
document.header.emit_key_and_value(
|
||||
document
|
||||
.contents
|
||||
.author
|
||||
.compound(kind)
|
||||
.compound(document.contents.created_at),
|
||||
1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn reduce(
|
||||
&self,
|
||||
mappings: &[bonsaidb::core::schema::ViewMappedValue<Self::View>],
|
||||
_rereduce: bool,
|
||||
) -> ReduceResult<Self::View> {
|
||||
Ok(mappings.iter().map(|m| m.value).sum())
|
||||
}
|
||||
}
|
||||
|
||||
impl CollectionViewSchema for PostsByTag {
|
||||
type View = Self;
|
||||
|
||||
fn map(
|
||||
&self,
|
||||
document: CollectionDocument<<Self::View as View>::Collection>,
|
||||
) -> ViewMapResult<Self::View> {
|
||||
if document.contents.tags.is_empty() {
|
||||
return Ok(Mappings::none());
|
||||
}
|
||||
|
||||
let mut mappings = Vec::new();
|
||||
for tag in document.contents.tags {
|
||||
mappings.extend(document.header.emit_key_and_value(tag, 1)?);
|
||||
}
|
||||
|
||||
Ok(Mappings::List(mappings))
|
||||
}
|
||||
|
||||
fn reduce(
|
||||
&self,
|
||||
mappings: &[bonsaidb::core::schema::ViewMappedValue<Self::View>],
|
||||
_rereduce: bool,
|
||||
) -> ReduceResult<Self::View> {
|
||||
Ok(mappings.iter().map(|m| m.value).sum())
|
||||
}
|
||||
}
|
||||
|
||||
impl CollectionViewSchema for PostsByMention {
|
||||
type View = Self;
|
||||
|
||||
fn map(
|
||||
&self,
|
||||
document: CollectionDocument<<Self::View as View>::Collection>,
|
||||
) -> ViewMapResult<Self::View> {
|
||||
if document.contents.mentions.is_empty() {
|
||||
return Ok(Mappings::none());
|
||||
}
|
||||
|
||||
let mut mappings = Vec::new();
|
||||
for mention in document.contents.mentions {
|
||||
mappings.extend(document.header.emit_key_and_value(mention, 1)?);
|
||||
}
|
||||
|
||||
Ok(Mappings::List(mappings))
|
||||
}
|
||||
|
||||
fn reduce(
|
||||
&self,
|
||||
mappings: &[bonsaidb::core::schema::ViewMappedValue<Self::View>],
|
||||
_rereduce: bool,
|
||||
) -> ReduceResult<Self::View> {
|
||||
Ok(mappings.iter().map(|m| m.value).sum())
|
||||
}
|
||||
}
|
||||
|
||||
impl CollectionViewSchema for PostsByParent {
|
||||
type View = Self;
|
||||
|
||||
fn map(
|
||||
&self,
|
||||
document: CollectionDocument<<Self::View as View>::Collection>,
|
||||
) -> ViewMapResult<Self::View> {
|
||||
document
|
||||
.header
|
||||
.emit_key_and_value(document.contents.reply_to, 1)
|
||||
}
|
||||
|
||||
fn reduce(
|
||||
&self,
|
||||
mappings: &[bonsaidb::core::schema::ViewMappedValue<Self::View>],
|
||||
_rereduce: bool,
|
||||
) -> ReduceResult<Self::View> {
|
||||
Ok(mappings.iter().map(|m| m.value).sum())
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue