add: auto_method macro
This commit is contained in:
parent
bb682add85
commit
1d9a96ae69
4 changed files with 184 additions and 117 deletions
|
@ -4,8 +4,7 @@ use crate::model::{
|
|||
auth::{Token, User},
|
||||
permissions::FinePermission,
|
||||
};
|
||||
use crate::{execute, get, query_row};
|
||||
|
||||
use crate::{auto_method, execute, get, query_row};
|
||||
use tetratto_shared::hash::hash_salted;
|
||||
|
||||
#[cfg(feature = "sqlite")]
|
||||
|
@ -32,50 +31,8 @@ impl DataManager {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get a user given just their `id`.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `id` - the ID of the user
|
||||
pub async fn get_user_by_id(&self, id: &str) -> Result<User> {
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = query_row!(&conn, "SELECT * FROM users WHERE id = $1", &[&id], |x| {
|
||||
Ok(Self::get_user_from_row(x))
|
||||
});
|
||||
|
||||
if res.is_err() {
|
||||
return Err(Error::UserNotFound);
|
||||
}
|
||||
|
||||
Ok(res.unwrap())
|
||||
}
|
||||
|
||||
/// Get a user given just their `username`.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `username` - the username of the user
|
||||
pub async fn get_user_by_username(&self, username: &str) -> Result<User> {
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = query_row!(
|
||||
&conn,
|
||||
"SELECT * FROM users WHERE username = $1",
|
||||
&[&username],
|
||||
|x| Ok(Self::get_user_from_row(x))
|
||||
);
|
||||
|
||||
if res.is_err() {
|
||||
return Err(Error::UserNotFound);
|
||||
}
|
||||
|
||||
Ok(res.unwrap())
|
||||
}
|
||||
auto_method!(get_user_by_id(&str)@get_user_from_row -> "SELECT * FROM users WHERE id = $1" --name="user" --returns=User);
|
||||
auto_method!(get_user_by_username(&str)@get_user_from_row -> "SELECT * FROM users WHERE username = $1" --name="user" --returns=User);
|
||||
|
||||
/// Get a user given just their auth token.
|
||||
///
|
||||
|
@ -176,27 +133,5 @@ impl DataManager {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Update the tokens of the the specified account (by `id`).
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `id` - the ID of the user
|
||||
/// * `tokens` - the **new** tokens vector for the user
|
||||
pub async fn update_user_tokens(&self, id: usize, tokens: Vec<Token>) -> Result<()> {
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = execute!(
|
||||
&conn,
|
||||
"UPDATE users SET tokens = $1 WHERE id = $2",
|
||||
&[&serde_json::to_string(&tokens).unwrap(), &id.to_string()]
|
||||
);
|
||||
|
||||
if let Err(e) = res {
|
||||
return Err(Error::DatabaseError(e.to_string()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
auto_method!(update_user_tokens(Vec<Token>) -> "UPDATE users SET tokens = $1 WHERE id = $2" --serde);
|
||||
}
|
||||
|
|
|
@ -19,3 +19,157 @@ impl DataManager {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! auto_method {
|
||||
($name:ident()@$select_fn:ident -> $query:literal --name=$name_:literal --returns=$returns_:tt) => {
|
||||
pub async fn $name(&self, id: usize) -> Result<$returns_> {
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = query_row!(&conn, $query, &[&id], |x| { Ok(Self::$select_fn(x)) });
|
||||
|
||||
if res.is_err() {
|
||||
return Err(Error::GeneralNotFound($name_.to_string()));
|
||||
}
|
||||
|
||||
Ok(res.unwrap())
|
||||
}
|
||||
};
|
||||
|
||||
($name:ident($selector_t:ty)@$select_fn:ident -> $query:literal --name=$name_:literal --returns=$returns_:tt) => {
|
||||
pub async fn $name(&self, selector: $selector_t) -> Result<$returns_> {
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = query_row!(&conn, $query, &[&selector], |x| { Ok(Self::$select_fn(x)) });
|
||||
|
||||
if res.is_err() {
|
||||
return Err(Error::GeneralNotFound($name_.to_string()));
|
||||
}
|
||||
|
||||
Ok(res.unwrap())
|
||||
}
|
||||
};
|
||||
|
||||
($name:ident()@$select_fn:ident:$permission:ident -> $query:literal) => {
|
||||
pub async fn $name(&self, id: usize, user: User) -> Result<()> {
|
||||
let page = self.$select_fn(id).await?;
|
||||
|
||||
if user.id != page.owner {
|
||||
if !user.permissions.check(FinePermission::$permission) {
|
||||
return Err(Error::NotAllowed);
|
||||
}
|
||||
}
|
||||
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = execute!(&conn, $query, &[&id.to_string()]);
|
||||
|
||||
if let Err(e) = res {
|
||||
return Err(Error::DatabaseError(e.to_string()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
($name:ident($x:ty)@$select_fn:ident:$permission:ident -> $query:literal) => {
|
||||
pub async fn $name(&self, id: usize, user: User, x: $x) -> Result<()> {
|
||||
let y = self.$select_fn(id).await?;
|
||||
|
||||
if user.id != y.owner {
|
||||
if !user.permissions.check(FinePermission::$permission) {
|
||||
return Err(Error::NotAllowed);
|
||||
}
|
||||
}
|
||||
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = execute!(&conn, $query, &[&x, &id.to_string()]);
|
||||
|
||||
if let Err(e) = res {
|
||||
return Err(Error::DatabaseError(e.to_string()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
($name:ident($x:ty)@$select_fn:ident:$permission:ident -> $query:literal --serde) => {
|
||||
pub async fn $name(&self, id: usize, user: User, x: $x) -> Result<()> {
|
||||
let y = self.$select_fn(id).await?;
|
||||
|
||||
if user.id != y.owner {
|
||||
if !user.permissions.check(FinePermission::$permission) {
|
||||
return Err(Error::NotAllowed);
|
||||
}
|
||||
}
|
||||
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = execute!(
|
||||
&conn,
|
||||
$query,
|
||||
&[&serde_json::to_string(&x).unwrap(), &id.to_string()]
|
||||
);
|
||||
|
||||
if let Err(e) = res {
|
||||
return Err(Error::DatabaseError(e.to_string()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
($name:ident($x:ty) -> $query:literal) => {
|
||||
pub async fn $name(&self, id: usize, x: $x) -> Result<()> {
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = execute!(&conn, $query, &[&x, &id.to_string()]);
|
||||
|
||||
if let Err(e) = res {
|
||||
return Err(Error::DatabaseError(e.to_string()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
($name:ident($x:ty) -> $query:literal --serde) => {
|
||||
pub async fn $name(&self, id: usize, x: $x) -> Result<()> {
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = execute!(
|
||||
&conn,
|
||||
$query,
|
||||
&[&serde_json::to_string(&x).unwrap(), &id.to_string()]
|
||||
);
|
||||
|
||||
if let Err(e) = res {
|
||||
return Err(Error::DatabaseError(e.to_string()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use super::*;
|
||||
use crate::model::auth::User;
|
||||
use crate::model::{Error, Result, journal::JournalPage, permissions::FinePermission};
|
||||
use crate::{execute, get, query_row};
|
||||
use crate::{auto_method, execute, get, query_row};
|
||||
|
||||
#[cfg(feature = "sqlite")]
|
||||
use rusqlite::Row;
|
||||
|
@ -26,26 +26,7 @@ impl DataManager {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get a journal page given just its `id`.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `id` - the ID of the page
|
||||
pub async fn get_page_by_id(&self, id: &str) -> Result<JournalPage> {
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = query_row!(&conn, "SELECT * FROM pages WHERE id = $1", &[&id], |x| {
|
||||
Ok(Self::get_page_from_row(x))
|
||||
});
|
||||
|
||||
if res.is_err() {
|
||||
return Err(Error::GeneralNotFound("journal page".to_string()));
|
||||
}
|
||||
|
||||
Ok(res.unwrap())
|
||||
}
|
||||
auto_method!(get_page_by_id()@get_page_from_row -> "SELECT * FROM pages WHERE id = $1" --name="journal page" --returns=JournalPage);
|
||||
|
||||
/// Create a new journal page in the database.
|
||||
///
|
||||
|
@ -96,31 +77,9 @@ impl DataManager {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a new user in the database.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `id` - the ID of the page
|
||||
/// * `user` - the user deleting the page
|
||||
pub async fn delete_page(&self, id: &str, user: User) -> Result<()> {
|
||||
let page = self.get_page_by_id(id).await?;
|
||||
|
||||
if user.id != page.owner {
|
||||
if !user.permissions.check(FinePermission::MANAGE_JOURNAL_PAGES) {
|
||||
return Err(Error::NotAllowed);
|
||||
}
|
||||
}
|
||||
|
||||
let conn = match self.connect().await {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||
};
|
||||
|
||||
let res = execute!(&conn, "DELETE FROM pages WHERE id = $1", &[&id]);
|
||||
|
||||
if let Err(e) = res {
|
||||
return Err(Error::DatabaseError(e.to_string()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
auto_method!(delete_page()@get_page_by_id:MANAGE_JOURNAL_PAGES -> "DELETE FROM pages WHERE id = $1");
|
||||
auto_method!(update_page_title(String)@get_page_by_id:MANAGE_JOURNAL_PAGES -> "UPDATE pages SET title = $1 WHERE id = $2");
|
||||
auto_method!(update_page_prompt(String)@get_page_by_id:MANAGE_JOURNAL_PAGES -> "UPDATE pages SET prompt = $1 WHERE id = $2");
|
||||
auto_method!(update_page_read_access(String)@get_page_by_id:MANAGE_JOURNAL_PAGES -> "UPDATE pages SET read_access = $1 WHERE id = $2" --serde);
|
||||
auto_method!(update_page_write_access(String)@get_page_by_id:MANAGE_JOURNAL_PAGES -> "UPDATE pages SET write_access = $1 WHERE id = $2" --serde);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use tetratto_shared::{snow::AlmostSnowflake, unix_epoch_timestamp};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct JournalPage {
|
||||
|
@ -17,6 +18,24 @@ pub struct JournalPage {
|
|||
pub write_access: JournalPageWriteAccess,
|
||||
}
|
||||
|
||||
impl JournalPage {
|
||||
/// Create a new [`JournalPage`].
|
||||
pub fn new(title: String, prompt: String, owner: usize) -> Self {
|
||||
Self {
|
||||
id: AlmostSnowflake::new(1234567890)
|
||||
.to_string()
|
||||
.parse::<usize>()
|
||||
.unwrap(),
|
||||
created: unix_epoch_timestamp() as usize,
|
||||
title,
|
||||
prompt,
|
||||
owner,
|
||||
read_access: JournalPageReadAccess::default(),
|
||||
write_access: JournalPageWriteAccess::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Who can read a [`JournalPage`].
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum JournalPageReadAccess {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue