Nicole Tietz-Sokolskaya
e0653e4bdd
This is a big dump of a lot of code, mostly making it so that: - we have a key-value store - we can create/save/load projects and documents - there's a sidebar layout with some placeholders we may or may not need - other stuff I forgot Reviewed-on: #1
190 lines
5.3 KiB
Rust
190 lines
5.3 KiB
Rust
use core::fmt::{self, Display};
|
|
use crate::prelude::*;
|
|
|
|
use anyhow::Result;
|
|
use fjall::PartitionHandle;
|
|
use model_derive::Model;
|
|
use serde::{Deserialize, Serialize};
|
|
use uuid::Uuid;
|
|
|
|
use crate::kv::KvHandle;
|
|
|
|
pub trait Model: Sized + Serialize + for<'a> Deserialize<'a> {
|
|
type Id;
|
|
|
|
fn id(&self) -> Self::Id;
|
|
fn key(id: Self::Id) -> Vec<u8>;
|
|
fn partition(kv_handle: &KvHandle) -> &PartitionHandle;
|
|
|
|
fn save(&self, kv_handle: &KvHandle) -> Result<()> {
|
|
let key = Self::key(self.id());
|
|
let value = bincode::serialize(self)?;
|
|
let partition = Self::partition(kv_handle);
|
|
|
|
partition.insert(key, value)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn load(kv_handle: &KvHandle, id: Self::Id) -> Result<Option<Self>> {
|
|
let key = Self::key(id);
|
|
let partition = Self::partition(kv_handle);
|
|
|
|
match partition.get(key.as_slice())? {
|
|
Some(bytes) => {
|
|
let bytes = bytes.to_vec();
|
|
let value: Self = bincode::deserialize(&bytes)?;
|
|
Ok(Some(value))
|
|
}
|
|
None => Ok(None),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Model, Serialize, Deserialize, PartialEq)]
|
|
#[model_version(0)]
|
|
pub struct Project {
|
|
pub id: Uuid,
|
|
pub owner_id: i32,
|
|
|
|
pub name: String,
|
|
pub description: String,
|
|
|
|
// The key is the short code, like BUG, which is used to refer to a project
|
|
// quickly and to display it more compactly. This must be unique across the
|
|
// projects a user owns.
|
|
pub key: String,
|
|
}
|
|
|
|
#[derive(Debug, Model, Serialize, Deserialize, PartialEq)]
|
|
#[model_version(0)]
|
|
pub struct Document {
|
|
pub id: Uuid,
|
|
pub project_id: Uuid,
|
|
|
|
pub title: String,
|
|
pub content: String,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
#[repr(u8)]
|
|
pub enum ModelType {
|
|
Project = 0,
|
|
Document = 1,
|
|
}
|
|
|
|
impl Display for ModelType {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
ModelType::Project => write!(f, "project"),
|
|
ModelType::Document => write!(f, "document"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
#[repr(u8)]
|
|
pub enum Permission {
|
|
Admin = 0,
|
|
Read = 1,
|
|
}
|
|
|
|
impl Display for Permission {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Permission::Admin => write!(f, "admin"),
|
|
Permission::Read => write!(f, "read"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
pub struct ModelPermission {
|
|
pub user_id: i32,
|
|
pub model_type: ModelType,
|
|
pub role: Permission,
|
|
pub model_id: Uuid,
|
|
}
|
|
|
|
impl ModelPermission {
|
|
pub fn add(&self, kv_handle: &KvHandle) -> Result<()> {
|
|
let key = format!(
|
|
"{}:{}:{}:{}",
|
|
self.user_id, self.model_type, self.role, self.model_id
|
|
);
|
|
let value = bincode::serialize(self)?;
|
|
|
|
kv_handle.permissions.insert(key, value)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn user_projects(kv_handle: &KvHandle, user_id: i32) -> Result<Vec<Project>> {
|
|
let prefix = format!("{}:{}:", user_id, ModelType::Project);
|
|
|
|
let mut ids = vec![];
|
|
for row in kv_handle.permissions.prefix(prefix).into_iter() {
|
|
let (_key, value) = row?;
|
|
let permission: ModelPermission = bincode::deserialize(&value)?;
|
|
ids.push(permission.model_id);
|
|
}
|
|
|
|
let projects: Vec<Project> = ids
|
|
.into_iter()
|
|
.filter_map(|id| {
|
|
let res = Project::load(kv_handle, id);
|
|
res.ok().flatten()
|
|
})
|
|
.collect();
|
|
|
|
Ok(projects)
|
|
}
|
|
|
|
pub fn user_project(kv_handle: &KvHandle, user_id: i32, project_id: Uuid) -> Result<Option<Project>> {
|
|
let key = format!("{}:{}:{}:{}", user_id, ModelType::Project, Permission::Admin, project_id);
|
|
let value = kv_handle.permissions.get(key)?;
|
|
|
|
match value {
|
|
Some(value) => {
|
|
let permission: ModelPermission = bincode::deserialize(&value)?;
|
|
let project = Project::load(kv_handle, permission.model_id)?;
|
|
Ok(project)
|
|
}
|
|
None => Ok(None),
|
|
}
|
|
}
|
|
|
|
pub fn user_documents(kv_handle: &KvHandle, user_id: i32) -> Result<Vec<Document>> {
|
|
let prefix = format!("{}:{}:", user_id, ModelType::Document);
|
|
|
|
let mut ids = vec![];
|
|
for row in kv_handle.permissions.prefix(prefix).into_iter() {
|
|
let (_key, value) = row?;
|
|
let permission: ModelPermission = bincode::deserialize(&value)?;
|
|
ids.push(permission.model_id);
|
|
}
|
|
dbg!(&ids);
|
|
|
|
let documents: Vec<Document> = ids
|
|
.into_iter()
|
|
.filter_map(|id| Document::load(kv_handle, id).ok().flatten())
|
|
.collect();
|
|
dbg!(&documents);
|
|
|
|
Ok(documents)
|
|
}
|
|
|
|
pub fn user_document(kv_handle: &KvHandle, user_id: i32, document_id: Uuid) -> Result<Option<Document>> {
|
|
let key = format!("{}:{}:{}:{}", user_id, ModelType::Document, Permission::Admin, document_id);
|
|
let value = kv_handle.permissions.get(key)?;
|
|
|
|
match value {
|
|
Some(value) => {
|
|
let permission: ModelPermission = bincode::deserialize(&value)?;
|
|
let document = Document::load(kv_handle, permission.model_id)?;
|
|
Ok(document)
|
|
}
|
|
None => Ok(None),
|
|
}
|
|
}
|
|
}
|