pique/src/models.rs
Nicole Tietz-Sokolskaya e0653e4bdd Create project and documents (#1)
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
2024-05-21 12:59:04 +00:00

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),
}
}
}