object parsing trait (no typechecking)

db-macros
Ashelyn Dawn 3 months ago committed by Ashelyn Rose
parent 9943050755
commit b63e79c16f
No known key found for this signature in database
GPG Key ID: D1980B8C6F349BC1

@ -13,3 +13,4 @@ serde = { version = "1", features = ["derive"] }
time = { version = "0.3.34" }
uuid = { version = "1.7.0", features = ["v4", "serde"] }
serde_json = "1.0.114"

@ -0,0 +1,3 @@
[toolchain]
channel = "nightly"

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

@ -1,3 +1,5 @@
#![feature(type_alias_impl_trait)]
#[macro_use]
extern crate rocket;

Loading…
Cancel
Save