Refactor comment serialization/deserialization

main
Elnu 2 years ago
parent f153625d1a
commit 6419b6357c

1
Cargo.lock generated

@ -324,6 +324,7 @@ dependencies = [
"libc", "libc",
"num-integer", "num-integer",
"num-traits", "num-traits",
"serde",
"time 0.1.44", "time 0.1.44",
"winapi", "winapi",
] ]

@ -13,4 +13,4 @@ serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
validator = { version = "0.15.0", features = ["derive"] } validator = { version = "0.15.0", features = ["derive"] }
md5 = "0.7.0" md5 = "0.7.0"
chrono = "0.4.19" chrono = { version = "0.4.19", features = ["serde"] }

@ -1,54 +1,31 @@
use chrono::NaiveDateTime; use chrono::serde::ts_seconds_option;
use serde::{Deserialize, Serialize}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize, Serializer};
use validator::Validate; use validator::Validate;
// Master comment type that is stored in database // Master comment type that is stored in database
#[derive(Serialize, Deserialize, Validate)]
pub struct Comment { pub struct Comment {
pub author: Option<String>, // None/null is Anonymous #[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>, pub author: Option<String>, // None is Anonymous
pub text: String, #[serde(rename(serialize = "gravatar"))]
pub timestamp: Option<NaiveDateTime>, #[serde(serialize_with = "serialize_gravatar")]
}
impl Comment {
pub fn send(&self) -> CommentSend {
CommentSend {
author: self.author.clone(),
gravatar: match self.email.clone() {
Some(email) => Some(format!("{:x}", md5::compute(email.to_lowercase()))),
None => None,
},
text: self.text.clone(),
timestamp: self.timestamp.unwrap().timestamp(),
}
}
}
// Comment type for API responses
#[derive(Serialize)]
pub struct CommentSend {
pub author: Option<String>,
pub gravatar: Option<String>,
pub text: String,
pub timestamp: i64,
}
// Comment type received containing new comment data
#[derive(Deserialize, Validate)]
pub struct CommentReceive {
pub author: Option<String>,
#[validate(email)] #[validate(email)]
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>, pub email: Option<String>,
#[validate(length(min = 1))]
pub text: String, pub text: String,
#[serde(default)]
#[serde(with = "ts_seconds_option")]
#[serde(skip_serializing_if = "Option::is_none")]
pub timestamp: Option<DateTime<Utc>>,
} }
impl CommentReceive { fn serialize_gravatar<S>(email: &Option<String>, s: S) -> Result<S::Ok, S::Error>
pub fn to_master(&self) -> Comment { where S: Serializer,
Comment { {
author: self.author.clone(), match email {
email: self.email.clone(), Some(email) => s.serialize_some(&format!("{:x}", md5::compute(email.to_lowercase()))),
text: self.text.clone(), None => s.serialize_none(),
timestamp: None,
}
} }
} }

@ -1,5 +1,4 @@
use crate::comment::{Comment, CommentSend}; use crate::Comment;
use chrono::NaiveDateTime;
use rusqlite::{params, Connection, Result}; use rusqlite::{params, Connection, Result};
pub struct Database { pub struct Database {
@ -25,20 +24,15 @@ impl Database {
Ok(Self { conn }) Ok(Self { conn })
} }
pub fn get_send_comments(&self) -> Result<Vec<CommentSend>> { pub fn get_comments(&self) -> Result<Vec<Comment>> {
self.conn self.conn
.prepare("SELECT author, email, text, timestamp FROM comment ORDER BY timestamp DESC")? .prepare("SELECT author, email, text, timestamp FROM comment ORDER BY timestamp DESC")?
.query_map([], |row| { .query_map([], |row| {
let timestamp: NaiveDateTime = row.get(3)?; Ok(Comment {
let timestamp = timestamp.timestamp();
Ok(CommentSend {
author: row.get(0)?, author: row.get(0)?,
gravatar: match row.get::<usize, Option<String>>(1)? { email: row.get(1)?,
Some(email) => Some(format!("{:x}", md5::compute(email.to_lowercase()))),
None => None,
},
text: row.get(2)?, text: row.get(2)?,
timestamp: timestamp, timestamp: row.get(3)?,
}) })
})? })?
.collect() .collect()

@ -19,7 +19,7 @@ async fn get_comments(data: web::Data<AppState>) -> impl Responder {
Ok(db) => db, Ok(db) => db,
Err(_) => return HttpResponse::InternalServerError().into(), Err(_) => return HttpResponse::InternalServerError().into(),
}; };
HttpResponse::Ok().json(&db.get_send_comments().unwrap()) HttpResponse::Ok().json(&db.get_comments().unwrap())
} }
#[post("/")] #[post("/")]
@ -30,14 +30,14 @@ async fn post_comment(data: web::Data<AppState>, bytes: web::Bytes) -> impl Resp
Ok(db) => db, Ok(db) => db,
Err(_) => return HttpResponse::InternalServerError(), Err(_) => return HttpResponse::InternalServerError(),
}; };
let comment: CommentReceive = match serde_json::from_str(&text) { let comment: Comment = match serde_json::from_str(&text) {
Ok(comment) => comment, Ok(comment) => comment,
Err(_) => return HttpResponse::BadRequest(), Err(_) => return HttpResponse::BadRequest(),
}; };
if comment.validate().is_err() { if comment.validate().is_err() {
return HttpResponse::BadRequest(); return HttpResponse::BadRequest();
} }
db.create_comment(&comment.to_master()).unwrap(); db.create_comment(&comment).unwrap();
HttpResponse::Ok() HttpResponse::Ok()
} }
Err(_) => HttpResponse::BadRequest().into(), Err(_) => HttpResponse::BadRequest().into(),

Loading…
Cancel
Save