diff --git a/Cargo.lock b/Cargo.lock index cc82983..f639d11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1721,9 +1721,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "opaque-debug" @@ -2937,9 +2937,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.16.2" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd30deba9a1cd7153c22aecf93e86df639e7b81c622b0af8d9255e989991a7b7" +checksum = "a34cc558345efd7e88b9eda9626df2138b80bb46a7606f695e751c892bc7dac6" dependencies = [ "indexmap", "itertools", @@ -3003,9 +3003,9 @@ dependencies = [ [[package]] name = "typed-sled" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b903ab728fcd56542a5c538dfc3d196aeffcdb1dc37e34c49f61782adc5cfa" +checksum = "1060f05a4450ec5b758da60951b04f225a93a62079316630e76cf25c4034500d" dependencies = [ "bincode", "pin-project", diff --git a/Cargo.toml b/Cargo.toml index a36724b..db7ae3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,8 +29,8 @@ sled = "0.34.7" tera = { version = "1.17.1", features = ["builtins", "date-locale"] } tide = { version = "0.16.0", default-features = false, features = ["h1-server", "cookies", "logger"] } tokio = { version = "1.23.0", features = ["macros", "rt-multi-thread"] } -toml_edit = { version = "0.16.2", features = ["easy"] } -typed-sled = "0.2.1" +toml_edit = { version = "0.17.1", features = ["easy"] } +typed-sled = "0.2.3" unic-langid = { version = "0.9.1", features = ["macros"] } [features] diff --git a/src/db.rs b/src/db.rs index c4b08f7..8fc1f72 100644 --- a/src/db.rs +++ b/src/db.rs @@ -2,6 +2,10 @@ use base64::engine::Engine; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use std::{net::IpAddr, path::Path}; + +pub use sled::transaction::{ + ConflictableTransactionError, ConflictableTransactionResult, TransactionError, +}; pub use typed_sled::Tree; const DB_DIR: &str = "db"; diff --git a/src/helpers.rs b/src/helpers.rs index 8ca1b6a..ee3c369 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -3,6 +3,7 @@ use crate::{config::Config, db::*, locales::Locales, queries::*}; use fluent_bundle::FluentArgs; use log::error; use std::{net::IpAddr, str::FromStr}; +use typed_sled::transaction::Transactional; use unic_langid::LanguageIdentifier; pub fn new_pending_comment( @@ -11,16 +12,21 @@ pub fn new_pending_comment( dbs: &Dbs, ) -> Result { let comment_id = CommentId::new(); - dbs.comment - .insert(&comment_id, &(comment.clone(), CommentStatus::Pending))?; - dbs.comment_pending.insert( - &( - comment.topic_hash.clone(), - comment.post_time, - comment_id.clone(), - ), - &(addr, false), - )?; + (&dbs.comment, &dbs.comment_pending) + .transaction(|(db_comment, db_comment_pending)| { + db_comment.insert(&comment_id, &(comment.clone(), CommentStatus::Pending))?; + db_comment_pending.insert( + &( + comment.topic_hash.clone(), + comment.post_time, + comment_id.clone(), + ), + &(addr, false), + )?; + ConflictableTransactionResult::<_>::Ok(()) + }) + .unwrap(); + Ok(comment_id) } @@ -40,18 +46,26 @@ pub fn edit_comment( // TODO should we update ip address in comment_pending? } CommentStatus::Approved => { - dbs.comment_pending.insert( - &( - edited_comment.topic_hash.clone(), - edited_comment.post_time, - comment_id.clone(), - ), - &(addr, true), - )?; - dbs.comment.insert( - &comment_id, - &(old_comment, CommentStatus::ApprovedEdited(edited_comment)), - )?; + (&dbs.comment, &dbs.comment_pending) + .transaction(|(db_comment, db_comment_pending)| { + db_comment_pending.insert( + &( + edited_comment.topic_hash.clone(), + edited_comment.post_time, + comment_id.clone(), + ), + &(addr, true), + )?; + db_comment.insert( + &comment_id, + &( + old_comment.clone(), + CommentStatus::ApprovedEdited(edited_comment.clone()), + ), + )?; + ConflictableTransactionResult::<_>::Ok(()) + }) + .unwrap(); } CommentStatus::ApprovedEdited(_old_edited_comment) => { dbs.comment.insert( @@ -65,94 +79,108 @@ pub fn edit_comment( } pub fn approve_comment(comment_id: CommentId, dbs: &Dbs) -> Result<(), sled::Error> { - if let Some((comment, CommentStatus::Pending)) = dbs.comment.get(&comment_id)? { - dbs.comment_pending.remove(&( - comment.topic_hash.clone(), - comment.post_time, - comment_id.clone(), - ))?; - dbs.comment_approved.insert( - &( - comment.topic_hash.clone(), - comment.post_time, - comment_id.clone(), - ), - &(), - )?; - dbs.comment - .insert(&comment_id, &(comment, CommentStatus::Approved))?; - } + (&dbs.comment, &dbs.comment_approved, &dbs.comment_pending) + .transaction(|(db_comment, db_comment_approved, db_comment_pending)| { + if let Some((comment, CommentStatus::Pending)) = db_comment.get(&comment_id)? { + db_comment_pending.remove(&( + comment.topic_hash.clone(), + comment.post_time, + comment_id.clone(), + ))?; + db_comment_approved.insert( + &( + comment.topic_hash.clone(), + comment.post_time, + comment_id.clone(), + ), + &(), + )?; + db_comment.insert(&comment_id, &(comment, CommentStatus::Approved))?; + } + ConflictableTransactionResult::<_>::Ok(()) + }) + .unwrap(); Ok(()) } pub fn approve_edit(comment_id: CommentId, dbs: &Dbs) -> Result, sled::Error> { - if let Some((comment, CommentStatus::ApprovedEdited(edited_comment))) = - dbs.comment.get(&comment_id)? - { - dbs.comment_pending.remove(&( - edited_comment.topic_hash.clone(), - edited_comment.post_time, - comment_id.clone(), - ))?; - dbs.comment - .insert(&comment_id, &(edited_comment, CommentStatus::Approved))?; - return Ok(Some(comment)); - } - Ok(None) + Ok((&dbs.comment, &dbs.comment_pending) + .transaction(|(db_comment, db_comment_pending)| { + if let Some((comment, CommentStatus::ApprovedEdited(edited_comment))) = + db_comment.get(&comment_id)? + { + db_comment_pending.remove(&( + edited_comment.topic_hash.clone(), + edited_comment.post_time, + comment_id.clone(), + ))?; + db_comment.insert(&comment_id, &(edited_comment, CommentStatus::Approved))?; + return ConflictableTransactionResult::<_>::Ok(Some(comment)); + } + ConflictableTransactionResult::<_>::Ok(None) + }) + .unwrap()) } pub fn remove_comment( comment_id: CommentId, dbs: &Dbs, ) -> Result, sled::Error> { - if let Some((comment, edited_comment)) = dbs.comment.remove(&comment_id)? { - match &edited_comment { - CommentStatus::Pending => { - dbs.comment_pending.remove(&( - comment.topic_hash.clone(), - comment.post_time, - comment_id, - ))?; + Ok((&dbs.comment, &dbs.comment_approved, &dbs.comment_pending) + .transaction(|(db_comment, db_comment_approved, db_comment_pending)| { + if let Some((comment, edited_comment)) = db_comment.remove(&comment_id)? { + match &edited_comment { + CommentStatus::Pending => { + db_comment_pending.remove(&( + comment.topic_hash.clone(), + comment.post_time, + comment_id.clone(), + ))?; + } + CommentStatus::Approved => { + db_comment_approved.remove(&( + comment.topic_hash.clone(), + comment.post_time, + comment_id.clone(), + ))?; + } + CommentStatus::ApprovedEdited(edited_comment) => { + db_comment_pending.remove(&( + edited_comment.topic_hash.clone(), + edited_comment.post_time, + comment_id.clone(), + ))?; + db_comment_approved.remove(&( + comment.topic_hash.clone(), + comment.post_time, + comment_id.clone(), + ))?; + } + } + return ConflictableTransactionResult::<_>::Ok(Some((comment, edited_comment))); } - CommentStatus::Approved => { - dbs.comment_approved.remove(&( - comment.topic_hash.clone(), - comment.post_time, - comment_id, - ))?; - } - CommentStatus::ApprovedEdited(edited_comment) => { - dbs.comment_pending.remove(&( + ConflictableTransactionResult::<_>::Ok(None) + }) + .unwrap()) +} + +pub fn remove_edit(comment_id: CommentId, dbs: &Dbs) -> Result, sled::Error> { + Ok((&dbs.comment, &dbs.comment_pending) + .transaction(|(db_comment, db_comment_pending)| { + if let Some((comment, CommentStatus::ApprovedEdited(edited_comment))) = + db_comment.get(&comment_id)? + { + db_comment_pending.remove(&( edited_comment.topic_hash.clone(), edited_comment.post_time, comment_id.clone(), ))?; - dbs.comment_approved.remove(&( - comment.topic_hash.clone(), - comment.post_time, - comment_id, - ))?; + db_comment.insert(&comment_id, &(comment.clone(), CommentStatus::Approved))?; + return ConflictableTransactionResult::<_>::Ok(Some(comment)); } - } - return Ok(Some((comment, edited_comment))); - } - Ok(None) -} - -pub fn remove_edit(comment_id: CommentId, dbs: &Dbs) -> Result, sled::Error> { - if let Some((comment, CommentStatus::ApprovedEdited(edited_comment))) = - dbs.comment.get(&comment_id)? - { - dbs.comment_pending.remove(&( - edited_comment.topic_hash.clone(), - edited_comment.post_time, - comment_id.clone(), - ))?; - dbs.comment - .insert(&comment_id, &(comment.clone(), CommentStatus::Approved))?; - return Ok(Some(comment)); - } - Ok(None) + ConflictableTransactionResult::<_>::Ok(None) + }) + .unwrap()) } pub fn iter_comments_by_topic<'a, V: typed_sled::KV>(