|
|
|
@ -1,5 +1,7 @@
|
|
|
|
|
use std::{iter::Peekable, vec::IntoIter};
|
|
|
|
|
|
|
|
|
|
use serde::Serialize;
|
|
|
|
|
use sqlx::{Pool, Postgres};
|
|
|
|
|
use sqlx::{postgres::PgRow, Pool, Postgres, Row};
|
|
|
|
|
use uuid::Uuid;
|
|
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
@ -23,11 +25,6 @@ pub struct Board {
|
|
|
|
|
description: String,
|
|
|
|
|
threads: Vec<Thread>
|
|
|
|
|
}
|
|
|
|
|
impl Board {
|
|
|
|
|
fn clone(&self) -> Board {
|
|
|
|
|
todo!()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Serialize, Clone)]
|
|
|
|
|
pub struct Thread {
|
|
|
|
@ -68,8 +65,8 @@ impl DB {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn get_site_data(&self) -> Result<Site, String> {
|
|
|
|
|
let rows = sqlx::query!(r#"
|
|
|
|
|
select
|
|
|
|
|
let rows : Vec<PgRow> = sqlx::query(
|
|
|
|
|
r#"select
|
|
|
|
|
site_uuid,
|
|
|
|
|
site_title,
|
|
|
|
|
site_base_url,
|
|
|
|
@ -93,88 +90,205 @@ impl DB {
|
|
|
|
|
left join forum.user on post_author = user_uuid
|
|
|
|
|
"#).fetch_all(&self.connection_pool).await.expect("Could not connect to database");
|
|
|
|
|
|
|
|
|
|
let mut site : Option<Site> = None;
|
|
|
|
|
let mut last_board : Option<Board> = None;
|
|
|
|
|
let mut last_thread : Option<Thread> = None;
|
|
|
|
|
let mut last_post : Option<Post> = None;
|
|
|
|
|
|
|
|
|
|
for row in rows {
|
|
|
|
|
if site.is_none() || row.site_uuid.is_some() && row.site_uuid.unwrap() != site.as_ref().unwrap().uuid {
|
|
|
|
|
site = Some(Site {
|
|
|
|
|
uuid: row.site_uuid.unwrap(),
|
|
|
|
|
title: row.site_title.unwrap_or(String::new()),
|
|
|
|
|
base_url: row.site_base_url.unwrap_or(String::new()),
|
|
|
|
|
theme: row.site_theme.unwrap_or(String::new()),
|
|
|
|
|
boards: Vec::new()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
let mut rows_iter = rows.into_iter().peekable();
|
|
|
|
|
|
|
|
|
|
if last_board.is_none() || row.board_uuid.is_some() && row.board_uuid.unwrap() != last_board.as_ref().unwrap().uuid {
|
|
|
|
|
if let Some(ref board) = last_board {
|
|
|
|
|
site.as_mut().unwrap().boards.push(board.clone())
|
|
|
|
|
}
|
|
|
|
|
let site = Site::parse_from_rows(&mut rows_iter, "site_".to_string());
|
|
|
|
|
|
|
|
|
|
if row.board_uuid.is_some() {
|
|
|
|
|
last_board = Some(Board {
|
|
|
|
|
uuid: row.board_uuid.unwrap(),
|
|
|
|
|
title: row.board_title.unwrap_or(String::new()),
|
|
|
|
|
description: row.board_description.unwrap_or(String::new()),
|
|
|
|
|
threads: Vec::new()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if let Some(site) = site {
|
|
|
|
|
Ok(site)
|
|
|
|
|
} else {
|
|
|
|
|
Err("Could not find site in DB".to_string())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Rows = Peekable<IntoIter<PgRow>>;
|
|
|
|
|
trait ParseFromRows {
|
|
|
|
|
fn parse_from_rows<'a>(rows: &'a mut Rows, item_prefix: String) -> Option<Self> where Self: Sized;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
macro_rules! parse_field {
|
|
|
|
|
( $row_name:ident, $item_prefix:ident, $field_name:literal ) => {
|
|
|
|
|
$row_name.get(format!("{}{}", $item_prefix, $field_name).as_str())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if last_thread.is_none() || row.thread_uuid.is_some() && row.thread_uuid.unwrap() != last_thread.as_mut().unwrap().uuid {
|
|
|
|
|
if let Some(ref thread) = last_thread {
|
|
|
|
|
last_board.as_mut().unwrap().threads.push(thread.clone())
|
|
|
|
|
macro_rules! parse_array {
|
|
|
|
|
( $rows:ident, $current_uuid:ident, $uuid_field:ident, $array_type:ty, $field_prefix:literal) => {
|
|
|
|
|
{
|
|
|
|
|
let mut items = Vec::new();
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
if let Some(item) = <$array_type>::parse_from_rows($rows, $field_prefix.to_string()) {
|
|
|
|
|
items.push(item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if row.thread_uuid.is_some() {
|
|
|
|
|
last_thread = Some(Thread {
|
|
|
|
|
uuid: row.thread_uuid.unwrap(),
|
|
|
|
|
title: row.thread_title.unwrap_or(String::new()),
|
|
|
|
|
posts: Vec::new()
|
|
|
|
|
})
|
|
|
|
|
$rows.next();
|
|
|
|
|
let next = $rows.peek();
|
|
|
|
|
|
|
|
|
|
if next.is_none() {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if last_post.is_none() || row.post_uuid.is_some() && row.post_uuid.unwrap() != last_post.as_ref().unwrap().uuid {
|
|
|
|
|
if let Some(ref post) = last_post {
|
|
|
|
|
last_thread.as_mut().unwrap().posts.push(post.clone())
|
|
|
|
|
let uuid_result = next.unwrap().try_get::<Option<Uuid>, &str>(&$uuid_field.as_str());
|
|
|
|
|
|
|
|
|
|
if uuid_result.is_err() || uuid_result.as_ref().unwrap().is_none() {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if row.post_uuid.is_some() {
|
|
|
|
|
last_post = Some(Post {
|
|
|
|
|
uuid: row.post_uuid.unwrap(),
|
|
|
|
|
contents: row.post_contents.unwrap_or(String::new()),
|
|
|
|
|
author: User {
|
|
|
|
|
uuid: row.user_uuid.unwrap(),
|
|
|
|
|
email: row.user_email.unwrap_or(String::new()),
|
|
|
|
|
username: row.user_username.unwrap_or(String::new()),
|
|
|
|
|
password_hash: row.user_password_hash.unwrap_or(String::new()),
|
|
|
|
|
is_admin: row.user_is_admin.unwrap_or(false)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
let next_uuid = uuid_result.as_ref().unwrap().unwrap().clone();
|
|
|
|
|
|
|
|
|
|
if next_uuid != $current_uuid {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
items
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if last_post.is_some() {
|
|
|
|
|
last_thread.as_mut().unwrap().posts.push(last_post.unwrap());
|
|
|
|
|
impl ParseFromRows for User {
|
|
|
|
|
fn parse_from_rows(rows: &mut Rows, item_prefix: String) -> Option<Self> {
|
|
|
|
|
let next = rows.peek();
|
|
|
|
|
if next.is_none() {
|
|
|
|
|
return None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if last_thread.is_some() {
|
|
|
|
|
last_board.as_mut().unwrap().threads.push(last_thread.unwrap());
|
|
|
|
|
let next = next.unwrap();
|
|
|
|
|
let uuid_field = format!("{}uuid", item_prefix) ;
|
|
|
|
|
let uuid_result = next.try_get::<Option<Uuid>, &str>(&uuid_field.as_str());
|
|
|
|
|
|
|
|
|
|
if uuid_result.is_err() || uuid_result.as_ref().unwrap().is_none() {
|
|
|
|
|
return None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if last_board.is_some() {
|
|
|
|
|
site.as_mut().unwrap().boards.push(last_board.unwrap());
|
|
|
|
|
let uuid = uuid_result.as_ref().unwrap().unwrap().clone();
|
|
|
|
|
let email = parse_field!(next, item_prefix, "email");
|
|
|
|
|
let username = parse_field!(next, item_prefix, "username");
|
|
|
|
|
let password_hash = parse_field!(next, item_prefix, "password_hash");
|
|
|
|
|
let is_admin = parse_field!(next, item_prefix, "is_admin");
|
|
|
|
|
|
|
|
|
|
Some(Self {
|
|
|
|
|
uuid,
|
|
|
|
|
email,
|
|
|
|
|
username,
|
|
|
|
|
password_hash,
|
|
|
|
|
is_admin
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ParseFromRows for Post {
|
|
|
|
|
fn parse_from_rows(rows: &mut Rows, item_prefix: String) -> Option<Self> {
|
|
|
|
|
let next = rows.peek();
|
|
|
|
|
if next.is_none() {
|
|
|
|
|
return None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(site) = site {
|
|
|
|
|
Ok(site)
|
|
|
|
|
} else {
|
|
|
|
|
Err("Could not find site in DB".to_string())
|
|
|
|
|
let next = next.unwrap();
|
|
|
|
|
let uuid_field = format!("{}uuid", item_prefix) ;
|
|
|
|
|
let uuid_result = next.try_get::<Option<Uuid>, &str>(&uuid_field.as_str());
|
|
|
|
|
|
|
|
|
|
if uuid_result.is_err() || uuid_result.as_ref().unwrap().is_none() {
|
|
|
|
|
return None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let uuid = uuid_result.as_ref().unwrap().unwrap().clone();
|
|
|
|
|
let contents = parse_field!(next, item_prefix, "contents");
|
|
|
|
|
let author = User::parse_from_rows(rows, "user_".to_string()).unwrap();
|
|
|
|
|
|
|
|
|
|
Some(Self {
|
|
|
|
|
uuid,
|
|
|
|
|
author,
|
|
|
|
|
contents
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ParseFromRows for Thread {
|
|
|
|
|
fn parse_from_rows(rows: &mut Rows, item_prefix: String) -> Option<Self> {
|
|
|
|
|
let next = rows.peek();
|
|
|
|
|
if next.is_none() {
|
|
|
|
|
return None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let next = next.unwrap();
|
|
|
|
|
let uuid_field = format!("{}uuid", item_prefix) ;
|
|
|
|
|
let uuid_result = next.try_get::<Option<Uuid>, &str>(&uuid_field.as_str());
|
|
|
|
|
|
|
|
|
|
if uuid_result.is_err() || uuid_result.as_ref().unwrap().is_none() {
|
|
|
|
|
return None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let uuid = uuid_result.as_ref().unwrap().unwrap().clone();
|
|
|
|
|
let title = parse_field!(next, item_prefix, "title");
|
|
|
|
|
let posts = parse_array!(rows, uuid, uuid_field, Post, "post_");
|
|
|
|
|
|
|
|
|
|
Some(Self {
|
|
|
|
|
uuid,
|
|
|
|
|
title,
|
|
|
|
|
posts
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ParseFromRows for Board {
|
|
|
|
|
fn parse_from_rows(rows: &mut Rows, item_prefix: String) -> Option<Self> {
|
|
|
|
|
let next = rows.peek();
|
|
|
|
|
if next.is_none() {
|
|
|
|
|
return None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let next = next.unwrap();
|
|
|
|
|
let uuid_field = format!("{}uuid", item_prefix) ;
|
|
|
|
|
let uuid_result = next.try_get::<Option<Uuid>, &str>(&uuid_field.as_str());
|
|
|
|
|
|
|
|
|
|
if uuid_result.is_err() || uuid_result.as_ref().unwrap().is_none() {
|
|
|
|
|
return None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let uuid = uuid_result.as_ref().unwrap().unwrap().clone();
|
|
|
|
|
let title = parse_field!(next, item_prefix, "title");
|
|
|
|
|
let description = parse_field!(next, item_prefix, "description");
|
|
|
|
|
let threads = parse_array!(rows, uuid, uuid_field, Thread, "thread_");
|
|
|
|
|
|
|
|
|
|
Some(Self {
|
|
|
|
|
uuid,
|
|
|
|
|
title,
|
|
|
|
|
description,
|
|
|
|
|
threads
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ParseFromRows for Site {
|
|
|
|
|
fn parse_from_rows(rows: &mut Rows, item_prefix: String) -> Option<Self> {
|
|
|
|
|
let next = rows.peek();
|
|
|
|
|
if next.is_none() {
|
|
|
|
|
return None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let next = next.unwrap();
|
|
|
|
|
let uuid_field = format!("{}uuid", item_prefix) ;
|
|
|
|
|
let uuid_result = next.try_get::<Option<Uuid>, &str>(&uuid_field.as_str());
|
|
|
|
|
|
|
|
|
|
if uuid_result.is_err() || uuid_result.as_ref().unwrap().is_none() {
|
|
|
|
|
return None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let uuid = uuid_result.as_ref().unwrap().unwrap().clone();
|
|
|
|
|
let title = parse_field!(next, item_prefix, "title");
|
|
|
|
|
let base_url = parse_field!(next, item_prefix, "base_url");
|
|
|
|
|
let theme = parse_field!(next, item_prefix, "theme");
|
|
|
|
|
let boards = parse_array!(rows, uuid, uuid_field, Board, "board_");
|
|
|
|
|
|
|
|
|
|
Some(Self {
|
|
|
|
|
uuid,
|
|
|
|
|
title,
|
|
|
|
|
base_url,
|
|
|
|
|
theme,
|
|
|
|
|
boards
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|