hyaenidae2/schema/src/lib.rs
2022-05-03 21:33:39 -05:00

661 lines
16 KiB
Rust

pub mod keys;
use bonsaidb::core::{
document::{CollectionDocument, Emit},
schema::{
view::{map::Mappings, CollectionViewSchema},
Collection, ReduceResult, Schema, View, ViewMapResult,
},
};
use keys::{DatetimeKey, PostKind, ProfileKind, ReportState, UuidKey};
use serde::{Deserialize, Serialize};
#[derive(Debug, Schema)]
#[schema(
name = "SiteSchema",
collections = [
Account,
Profile,
Media,
Post,
Tag,
Report,
]
)]
pub struct SiteSchema;
#[derive(Collection, Debug, Deserialize, Serialize)]
#[collection(
name = "accounts",
views = [AccountsByUsername, AccountsByEmail],
primary_key = UuidKey,
natural_id = |account: &Account| Some(account.id)
)]
pub struct Account {
id: UuidKey,
username: String,
emails: Vec<String>,
}
#[derive(Collection, Debug, Deserialize, Serialize)]
#[collection(
name = "profiles",
views = [ProfilesByHandle, RemoteProfilesByDomain, LocalProfilesById, ProfilesByCollaborator, ProfilesByKind],
primary_key = UuidKey,
natural_id = |profile: &Profile| Some(profile.id)
)]
pub struct Profile {
id: UuidKey,
kind: ProfileKind,
handle: String,
domain: Option<String>,
full_url: Option<String>,
name: Option<String>,
description: Option<String>,
// Account Key
local_account: Option<UuidKey>,
// Profile Keys
collaborators: Vec<UuidKey>,
created_at: DatetimeKey,
updated_at: DatetimeKey,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(tag = "type")]
pub enum MediaState {
Uploading {
upload_id: String,
},
Finished {
filename: String,
delete_token: String,
},
}
#[derive(Collection, Debug, Deserialize, Serialize)]
#[collection(
name = "media",
views = [],
primary_key = UuidKey,
natural_id = |media: &Media| Some(media.id)
)]
pub struct Media {
id: UuidKey,
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,
}
#[derive(Collection, Debug, Deserialize, Serialize)]
#[collection(
name = "tags",
views = [TagsByName],
primary_key = UuidKey,
natural_id = |tag: &Tag| Some(tag.id)
)]
pub struct Tag {
id: UuidKey,
name: String,
}
#[derive(Collection, Debug, Deserialize, Serialize)]
#[collection(
name = "reports",
views = [ReportsByPost, ReportsByState, ReportsByAccount],
primary_key = UuidKey,
natural_id = |report: &Report| Some(report.id)
)]
pub struct Report {
id: UuidKey,
message: Option<String>,
moderation_note: Option<String>,
// Post key
post: UuidKey,
state: ReportState,
// Account Key
handled_by: Option<UuidKey>,
created_at: DatetimeKey,
updated_at: DatetimeKey,
}
#[derive(Debug, Clone, View)]
#[view(collection = Account, key = String, value = usize, name = "by-username")]
pub struct AccountsByUsername;
#[derive(Debug, Clone, View)]
#[view(collection = Account, key = String, value = usize, name = "by-email")]
pub struct AccountsByEmail;
#[derive(Debug, Clone, View)]
#[view(collection = Profile, key = String, value = usize, name = "by-handle")]
pub struct ProfilesByHandle;
#[derive(Debug, Clone, View)]
#[view(collection = Profile, key = Option<String>, value = usize, name = "by-remote-domain")]
pub struct RemoteProfilesByDomain;
#[derive(Debug, Clone, View)]
#[view(collection = Profile, key = Option<UuidKey>, value = usize, name = "by-local-id")]
pub struct LocalProfilesById;
#[derive(Debug, Clone, View)]
#[view(collection = Profile, key = UuidKey, 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")]
pub struct ReportsByPost;
#[derive(Debug, Clone, View)]
#[view(collection = Report, key = ReportState, value = usize, name = "by-state")]
pub struct ReportsByState;
#[derive(Debug, Clone, View)]
#[view(collection = Report, key = Option<UuidKey>, value = usize, name = "by-account")]
pub struct ReportsByAccount;
impl CollectionViewSchema for AccountsByUsername {
type View = Self;
fn map(
&self,
document: CollectionDocument<<Self::View as View>::Collection>,
) -> ViewMapResult<Self::View> {
document
.header
.emit_key_and_value(document.contents.username, 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 AccountsByEmail {
type View = Self;
fn map(
&self,
document: CollectionDocument<<Self::View as View>::Collection>,
) -> ViewMapResult<Self::View> {
if document.contents.emails.is_empty() {
return Ok(Mappings::Simple(None));
}
let mut mappings = Vec::new();
for email in document.contents.emails {
mappings.extend(document.header.emit_key_and_value(email, 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 ProfilesByHandle {
type View = Self;
fn map(
&self,
document: CollectionDocument<<Self::View as View>::Collection>,
) -> ViewMapResult<Self::View> {
let mut mappings = Vec::new();
if document.contents.handle.is_empty() {
return Ok(Mappings::Simple(None));
}
const MAX_SEARCH_LEN: usize = 16;
// enable searching for profiles by handle lol
// turns 'asonix' into 'a', 'as', 'aso', 'ason', 'asoni', 'asonix'
// cap search characters
for i in 1..=document.contents.handle.len().min(MAX_SEARCH_LEN) {
mappings.extend(
document
.header
.emit_key_and_value(document.contents.handle[..i].to_owned(), 1)?,
);
}
// Ensure we index the full handle if longer than max characters as well
if document.contents.handle.len() > MAX_SEARCH_LEN {
mappings.extend(
document
.header
.emit_key_and_value(document.contents.handle, 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 RemoteProfilesByDomain {
type View = Self;
fn map(
&self,
document: CollectionDocument<<Self::View as View>::Collection>,
) -> ViewMapResult<Self::View> {
document
.header
.emit_key_and_value(document.contents.domain, 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 LocalProfilesById {
type View = Self;
fn map(
&self,
document: CollectionDocument<<Self::View as View>::Collection>,
) -> ViewMapResult<Self::View> {
document
.header
.emit_key_and_value(document.contents.local_account, 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 ProfilesByCollaborator {
type View = Self;
fn map(
&self,
document: CollectionDocument<<Self::View as View>::Collection>,
) -> ViewMapResult<Self::View> {
if document.contents.collaborators.is_empty() {
return Ok(Mappings::Simple(None));
}
let mut mappings = Vec::new();
for collaborator in document.contents.collaborators {
mappings.extend(document.header.emit_key_and_value(collaborator, 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 ProfilesByKind {
type View = Self;
fn map(
&self,
document: CollectionDocument<<Self::View as View>::Collection>,
) -> ViewMapResult<Self::View> {
document
.header
.emit_key_and_value(document.contents.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 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;
fn map(
&self,
document: CollectionDocument<<Self::View as View>::Collection>,
) -> ViewMapResult<Self::View> {
document
.header
.emit_key_and_value(document.contents.name, 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 ReportsByPost {
type View = Self;
fn map(
&self,
document: CollectionDocument<<Self::View as View>::Collection>,
) -> ViewMapResult<Self::View> {
document
.header
.emit_key_and_value(document.contents.post, 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 ReportsByState {
type View = Self;
fn map(
&self,
document: CollectionDocument<<Self::View as View>::Collection>,
) -> ViewMapResult<Self::View> {
document
.header
.emit_key_and_value(document.contents.state, 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 ReportsByAccount {
type View = Self;
fn map(
&self,
document: CollectionDocument<<Self::View as View>::Collection>,
) -> ViewMapResult<Self::View> {
document
.header
.emit_key_and_value(document.contents.handled_by, 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 Tag {
pub fn new(name: String) -> Self {
Self {
id: UuidKey::generate(),
name,
}
}
pub fn name(&self) -> &str {
&self.name
}
}