hyaenidae/profiles/src/store/view/relationship.rs
asonix 642f0bb578 Profiles: lots of federation fixes
Currently federation requests + accepts + rejects + undos all work
Federation blocking works also
Profiles can federate their text and images, provided federation was
enabled before profile creation. Looking into backfilling existing
profiles is TODO
2021-01-17 01:27:13 -06:00

363 lines
11 KiB
Rust

use crate::store::{count, StoreError, Undo};
use chrono::{DateTime, Utc};
use sled::{Db, Transactional, Tree};
use uuid::Uuid;
#[derive(Debug, Clone)]
pub struct Relationship {
pub id: Uuid,
pub published: DateTime<Utc>,
pub left: Uuid,
pub right: Uuid,
}
#[derive(Debug, Clone)]
pub struct RelationshipStore {
name: String,
id_left: Tree,
id_right: Tree,
id_published: Tree,
forward_id: Tree,
forward_counts: Tree,
forward_published_id: Tree,
backward_id: Tree,
backward_counts: Tree,
backward_published_id: Tree,
}
#[derive(Debug, Clone)]
struct RelationshipKeys {
name: String,
}
impl RelationshipKeys {
fn id_key(&self, id: Uuid) -> String {
format!("/{}/id/{}/data", self.name, id)
}
fn forward_key(&self, left: Uuid, right: Uuid) -> String {
format!("/{}/id/{}/forward/{}", self.name, left, right)
}
fn forward_published_key(&self, left: Uuid, published: DateTime<Utc>, right: Uuid) -> String {
format!(
"/{}/id/{}/forward/published/{}/{}",
self.name,
left,
published.to_rfc3339(),
right
)
}
fn forward_published_prefix(&self, left: Uuid) -> String {
format!("/{}/id/{}/forward/published", self.name, left)
}
fn forward_counts_key(&self, left: Uuid) -> String {
format!("/{}/id/{}/forward/counts", self.name, left)
}
fn backward_key(&self, right: Uuid, left: Uuid) -> String {
format!("/{}/id/{}/backward/{}", self.name, right, left)
}
fn backward_published_key(&self, right: Uuid, published: DateTime<Utc>, left: Uuid) -> String {
format!(
"/{}/id/{}/backward/published/{}/{}",
self.name,
right,
published.to_rfc3339(),
left
)
}
fn backward_published_prefix(&self, right: Uuid) -> String {
format!("/{}/id/{}/backward/published", self.name, right)
}
fn backward_counts_key(&self, right: Uuid) -> String {
format!("/{}/id/{}/backward/counts", self.name, right)
}
}
impl RelationshipStore {
fn keys(&self) -> RelationshipKeys {
RelationshipKeys {
name: self.name.clone(),
}
}
pub(super) fn build(name: &str, db: &Db) -> Result<Self, sled::Error> {
Ok(RelationshipStore {
id_left: db.open_tree(&format!("/profiles/relationship/{}/id/left", name))?,
id_right: db.open_tree(&format!("/profiles/relationship/{}/id/right", name))?,
id_published: db.open_tree(&format!("/profiles/relationship/{}/id/published", name))?,
forward_id: db.open_tree(&format!("/profiles/relationship/{}/forward/id", name))?,
forward_published_id: db.open_tree(&format!(
"/profiles/relationship/{}/forward/published/id",
name
))?,
forward_counts: db
.open_tree(&format!("/profiles/relationship/{}/forward/counts", name))?,
backward_id: db.open_tree(&format!("/profiles/relationship/{}/backward/id", name))?,
backward_published_id: db.open_tree(&format!(
"/profiles/relationship/{}/backward/published/id",
name
))?,
backward_counts: db
.open_tree(&format!("/profiles/relationship/{}/backward/counts", name))?,
name: name.to_owned(),
})
}
pub fn new(
&self,
left_id: Uuid,
right_id: Uuid,
published: DateTime<Utc>,
) -> Result<Relationship, StoreError> {
let keys = self.keys();
let (id, published) = [
&self.id_left,
&self.id_right,
&self.id_published,
&self.forward_id,
&self.forward_published_id,
&self.forward_counts,
&self.backward_id,
&self.backward_published_id,
&self.backward_counts,
]
.transaction(move |trees| {
let id_left = &trees[0];
let id_right = &trees[1];
let id_published = &trees[2];
let forward_id = &trees[3];
let forward_published_id = &trees[4];
let forward_counts = &trees[5];
let backward_id = &trees[6];
let backward_published_id = &trees[7];
let backward_counts = &trees[8];
let opt = forward_id
.get(keys.forward_key(left_id, right_id).as_bytes())?
.and_then(|ivec| Uuid::from_slice(&ivec).ok());
let opt = match opt {
Some(id) => id_published
.get(keys.id_key(id).as_bytes())?
.and_then(|ivec| String::from_utf8_lossy(&ivec).parse::<DateTime<Utc>>().ok())
.map(|p| (id, p)),
None => None,
};
let (id, published) = if let Some((id, published)) = opt {
(id, published)
} else {
(Uuid::new_v4(), published)
};
id_left.insert(keys.id_key(id).as_bytes(), left_id.as_bytes())?;
id_right.insert(keys.id_key(id).as_bytes(), right_id.as_bytes())?;
id_published.insert(
keys.id_key(id).as_bytes(),
published.to_rfc3339().as_bytes(),
)?;
forward_id.insert(
keys.forward_key(left_id, right_id).as_bytes(),
id.as_bytes(),
)?;
backward_id.insert(
keys.backward_key(right_id, left_id).as_bytes(),
id.as_bytes(),
)?;
forward_published_id.insert(
keys.forward_published_key(left_id, published, right_id)
.as_bytes(),
right_id.as_bytes(),
)?;
backward_published_id.insert(
keys.backward_published_key(right_id, published, left_id)
.as_bytes(),
left_id.as_bytes(),
)?;
count(forward_counts, &keys.forward_counts_key(left_id), |c| {
c.saturating_add(1)
})?;
count(backward_counts, &keys.backward_counts_key(right_id), |c| {
c.saturating_add(1)
})?;
Ok((id, published))
})?;
Ok(Relationship {
id,
published,
left: left_id,
right: right_id,
})
}
pub fn forward_iter(&self, left_id: Uuid) -> impl DoubleEndedIterator<Item = Uuid> {
let keys = self.keys();
self.forward_published_id
.scan_prefix(keys.forward_published_prefix(left_id))
.values()
.filter_map(|res| res.ok())
.filter_map(|ivec| Uuid::from_slice(&ivec).ok())
}
pub fn backward_iter(&self, right_id: Uuid) -> impl DoubleEndedIterator<Item = Uuid> {
let keys = self.keys();
self.backward_published_id
.scan_prefix(keys.backward_published_prefix(right_id))
.values()
.filter_map(|res| res.ok())
.filter_map(|ivec| Uuid::from_slice(&ivec).ok())
}
pub fn by_forward(&self, left_id: Uuid, right_id: Uuid) -> Result<Option<Uuid>, StoreError> {
let keys = self.keys();
let opt = self
.forward_id
.get(keys.forward_key(left_id, right_id))?
.and_then(|ivec| Uuid::from_slice(&ivec).ok());
Ok(opt)
}
pub fn left(&self, id: Uuid) -> Result<Option<Uuid>, StoreError> {
let keys = self.keys();
let opt = self
.id_left
.get(keys.id_key(id))?
.and_then(|ivec| Uuid::from_slice(&ivec).ok());
Ok(opt)
}
pub fn right(&self, id: Uuid) -> Result<Option<Uuid>, StoreError> {
let keys = self.keys();
let opt = self
.id_right
.get(keys.id_key(id))?
.and_then(|ivec| Uuid::from_slice(&ivec).ok());
Ok(opt)
}
pub fn published(&self, id: Uuid) -> Result<Option<DateTime<Utc>>, StoreError> {
let keys = self.keys();
let opt = self
.id_published
.get(keys.id_key(id))?
.and_then(|ivec| String::from_utf8_lossy(&ivec).parse::<DateTime<Utc>>().ok());
Ok(opt)
}
pub fn by_id(&self, id: Uuid) -> Result<Option<Relationship>, StoreError> {
let left_opt = self.left(id)?;
let right_opt = self.right(id)?;
let published_opt = self.published(id)?;
let opt = left_opt.and_then(|left| {
right_opt.and_then(|right| {
published_opt.map(|published| Relationship {
id,
left,
right,
published,
})
})
});
Ok(opt)
}
pub fn remove(&self, id: Uuid) -> Result<Option<Undo<Relationship>>, StoreError> {
let keys = self.keys();
let left_id = match self.left(id)? {
Some(id) => id,
None => return Ok(None),
};
let right_id = match self.right(id)? {
Some(id) => id,
None => return Ok(None),
};
let published = match self.published(id)? {
Some(id) => id,
None => return Ok(None),
};
[
&self.id_left,
&self.id_right,
&self.id_published,
&self.forward_id,
&self.forward_published_id,
&self.forward_counts,
&self.backward_id,
&self.backward_published_id,
&self.backward_counts,
]
.transaction(move |trees| {
let id_left = &trees[0];
let id_right = &trees[1];
let id_published = &trees[2];
let forward_id = &trees[3];
let forward_published_id = &trees[4];
let forward_counts = &trees[5];
let backward_id = &trees[6];
let backward_published_id = &trees[7];
let backward_counts = &trees[8];
id_left.remove(keys.id_key(id).as_bytes())?;
id_right.remove(keys.id_key(id).as_bytes())?;
id_published.remove(keys.id_key(id).as_bytes())?;
forward_id.remove(keys.forward_key(left_id, right_id).as_bytes())?;
backward_id.remove(keys.backward_key(right_id, left_id).as_bytes())?;
forward_published_id.remove(
keys.forward_published_key(left_id, published, right_id)
.as_bytes(),
)?;
backward_published_id.remove(
keys.backward_published_key(right_id, published, left_id)
.as_bytes(),
)?;
count(forward_counts, &keys.forward_counts_key(left_id), |c| {
c.saturating_sub(1)
})?;
count(backward_counts, &keys.backward_counts_key(right_id), |c| {
c.saturating_sub(1)
})?;
Ok(())
})?;
Ok(Some(Undo(Relationship {
id,
left: left_id,
right: right_id,
published,
})))
}
}