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; 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> { 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> { 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 = 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> { 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> { 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 = 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> { 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), } } }