oh boy
This commit is contained in:
commit
14b428cc1c
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
server-data.bonsaidb
|
3117
Cargo.lock
generated
Normal file
3117
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "artsite"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bonsaidb = { version = "0.4.1", features = ["server-full", "client-full"] }
|
||||
schema = { path = "./schema" }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
|
||||
[workspace]
|
||||
members = ["schema"]
|
14
schema/Cargo.toml
Normal file
14
schema/Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "schema"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bonsaidb = "0.4.1"
|
||||
num-derive = "0.3"
|
||||
num-traits = "0.2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
time = { version = "0.3.9", features = ["serde", "serde-well-known"] }
|
||||
uuid = { version = "1", features = ["serde", "v4"] }
|
161
schema/src/keys.rs
Normal file
161
schema/src/keys.rs
Normal file
|
@ -0,0 +1,161 @@
|
|||
use bonsaidb::core::key::{EnumKey, Key, KeyEncoding};
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::{format_description::well_known::Rfc3339, OffsetDateTime, UtcOffset};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Hash,
|
||||
Deserialize,
|
||||
Serialize,
|
||||
FromPrimitive,
|
||||
ToPrimitive,
|
||||
)]
|
||||
pub enum ProfileKind {
|
||||
Server,
|
||||
Collaboration,
|
||||
Personal,
|
||||
Bot,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Hash,
|
||||
Deserialize,
|
||||
Serialize,
|
||||
FromPrimitive,
|
||||
ToPrimitive,
|
||||
)]
|
||||
pub enum PostKind {
|
||||
Media,
|
||||
Text,
|
||||
Comment,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Hash,
|
||||
Deserialize,
|
||||
Serialize,
|
||||
FromPrimitive,
|
||||
ToPrimitive,
|
||||
)]
|
||||
pub enum ReportState {
|
||||
Unresolved,
|
||||
Closed,
|
||||
MarkedSensitive,
|
||||
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 {
|
||||
pub fn generate() -> Self {
|
||||
Self {
|
||||
uuid: Uuid::new_v4(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
660
schema/src/lib.rs
Normal file
660
schema/src/lib.rs
Normal file
|
@ -0,0 +1,660 @@
|
|||
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
|
||||
}
|
||||
}
|
86
src/main.rs
Normal file
86
src/main.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
use bonsaidb::{
|
||||
client::{url::Url, Client},
|
||||
core::{
|
||||
connection::{AsyncConnection, AsyncStorageConnection},
|
||||
schema::SerializedCollection,
|
||||
},
|
||||
local::config::Builder,
|
||||
server::{DefaultPermissions, Server, ServerConfiguration},
|
||||
};
|
||||
use schema::{Account, SiteSchema};
|
||||
use std::time::Duration;
|
||||
|
||||
type Error = Box<dyn std::error::Error + Send + Sync>;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Error> {
|
||||
let server = Server::open(
|
||||
ServerConfiguration::new("server-data.bonsaidb")
|
||||
.default_permissions(DefaultPermissions::AllowAll)
|
||||
.with_schema::<SiteSchema>()?,
|
||||
)
|
||||
.await?;
|
||||
if server.certificate_chain().await.is_err() {
|
||||
server.install_self_signed_certificate(true).await?;
|
||||
}
|
||||
let certificate = server
|
||||
.certificate_chain()
|
||||
.await?
|
||||
.into_end_entity_certificate();
|
||||
server
|
||||
.create_database::<SiteSchema>("my-database", true)
|
||||
.await?;
|
||||
|
||||
let task_server = server.clone();
|
||||
tokio::spawn(async move { task_server.listen_on(5645).await });
|
||||
|
||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||
|
||||
tokio::spawn(async move {
|
||||
let client = Client::build(Url::parse("bonsaidb://localhost")?)
|
||||
.with_certificate(certificate)
|
||||
.finish()?
|
||||
.database::<SiteSchema>("site-db")
|
||||
.await?;
|
||||
|
||||
/*
|
||||
println!("inserting");
|
||||
|
||||
for i in 0..50 {
|
||||
let sides = i % 3 + 3;
|
||||
Shape::new(sides).push_into_async(&client).await?;
|
||||
}
|
||||
|
||||
println!("reducing");
|
||||
|
||||
for result in client
|
||||
.view::<ShapesByNumberOfSides>()
|
||||
.reduce_grouped()
|
||||
.await?
|
||||
{
|
||||
println!("sides: {}, count: {}", result.key, result.value);
|
||||
}
|
||||
|
||||
println!("fetching");
|
||||
|
||||
let six_sided = client
|
||||
.view::<ShapesByNumberOfSides>()
|
||||
.with_key(5)
|
||||
.query_with_docs()
|
||||
.await?;
|
||||
|
||||
for mapping in &six_sided {
|
||||
let _ = Shape::document_contents(mapping.document)?;
|
||||
}
|
||||
println!("fetched");
|
||||
*/
|
||||
Ok(()) as Result<(), Error>
|
||||
});
|
||||
|
||||
tokio::signal::ctrl_c().await?;
|
||||
|
||||
server.shutdown(Some(Duration::from_secs(5))).await?;
|
||||
println!("Hello, world!");
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in a new issue