diff --git a/Cargo.lock b/Cargo.lock index 51934dc..a931f1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -284,9 +284,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.87" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" dependencies = [ "backtrace", ] @@ -403,9 +403,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "bytestring" @@ -433,9 +433,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.18" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" dependencies = [ "jobserver", "libc", @@ -1066,9 +1066,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1614,9 +1614,9 @@ dependencies = [ [[package]] name = "postgres-types" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02048d9e032fb3cc3413bbf7b83a15d84a5d419778e2628751896d856498eee9" +checksum = "f66ea23a2d0e5734297357705193335e0a957696f34bed2f2faefacb2fec336f" dependencies = [ "bytes", "fallible-iterator", @@ -1738,9 +1738,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ "bitflags 2.6.0", ] @@ -1867,9 +1867,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.36" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags 2.6.0", "errno", @@ -2409,9 +2409,9 @@ dependencies = [ [[package]] name = "tokio-postgres" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03adcf0147e203b6032c0b2d30be1415ba03bc348901f3ff1cc0df6a733e60c3" +checksum = "3b5d3742945bc7d7f210693b0c58ae542c6fd47b17adbbda0885f3dcb34a6bdb" dependencies = [ "async-trait", "byteorder", @@ -2559,15 +2559,15 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] @@ -2580,9 +2580,9 @@ checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-truncate" diff --git a/src/comments.rs b/src/comments.rs index cd27df6..77183ce 100644 --- a/src/comments.rs +++ b/src/comments.rs @@ -1,5 +1,7 @@ //! Types and functions for post comments. +use std::cmp; + use tui::widgets::TableState; use crate::{ @@ -31,7 +33,7 @@ pub fn load_comments(file_name: &str) -> Result { /// Represents a response to a [Comment] API request. #[repr(C)] -#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, Eq, serde::Deserialize, serde::Serialize)] pub struct CommentResponse { pub comment: Comment, pub creator: Creator, @@ -42,6 +44,7 @@ pub struct CommentResponse { pub subscribed: String, pub saved: bool, pub creator_blocked: bool, + pub level: Option, } impl CommentResponse { @@ -57,8 +60,121 @@ impl CommentResponse { subscribed: String::new(), saved: false, creator_blocked: false, + level: None, } } + + /// Gets the comment level of the [CommentResponse]. + pub fn level(&self) -> usize { + self.level.unwrap_or_default() + } + + /// Sets the comment level of the [CommentResponse]. + pub fn set_level(&mut self, level: usize) { + self.level.replace(level); + } +} + +impl PartialEq for CommentResponse { + fn eq(&self, rhs: &Self) -> bool { + let self_ids_len = self.comment.path.split('.').count(); + let rhs_ids_len = rhs.comment.path.split('.').count(); + let min_level = cmp::min(self_ids_len - 1, rhs_ids_len - 1); + + let self_id = self + .comment + .path + .split('.') + .nth(min_level) + .map(|i| i.parse::().unwrap_or(0)); + let rhs_id = rhs + .comment + .path + .split('.') + .nth(min_level) + .map(|i| i.parse::().unwrap_or(0)); + + self.level == rhs.level + && self_id == rhs_id + && self.comment.published == rhs.comment.published + } +} + +impl PartialOrd for CommentResponse { + fn partial_cmp(&self, rhs: &Self) -> Option { + let self_id = self.comment.id; + + let self_pos = self + .comment + .path + .split('.') + .map(|c| c.parse::().unwrap_or(0)) + .position(|c| c == self_id) + .unwrap_or(0); + + let self_root = self + .comment + .path + .split('.') + .map(|c| c.parse::().unwrap_or(0)) + .nth(1); + + let rhs_id = rhs.comment.id; + + let rhs_pos = rhs + .comment + .path + .split('.') + .map(|c| c.parse::().unwrap_or(0)) + .position(|c| c == rhs_id) + .unwrap_or(0); + + let rhs_root = rhs + .comment + .path + .split('.') + .map(|c| c.parse::().unwrap_or(0)) + .nth(1); + + let level = cmp::min(self_pos.saturating_sub(1), rhs_pos.saturating_sub(1)); + + let ancestor_ord = self + .comment + .path + .split('.') + .skip(2) + .take(level.saturating_sub(2)) + .map(|c| c.parse::().unwrap_or(0)) + .zip( + rhs.comment + .path + .split('.') + .skip(2) + .take(level.saturating_sub(2)) + .map(|c| c.parse::().unwrap_or(0)), + ) + .fold(self_root.cmp(&rhs_root), |acc, (s, r)| acc.then(s.cmp(&r))); + + let self_child = self.counts.child_count(); + let rhs_child = rhs.counts.child_count(); + + let published = self.comment.published.as_str(); + let rhs_published = rhs.comment.published.as_str(); + + Some( + ancestor_ord + .then(self_pos.cmp(&rhs_pos)) + .then(self_child.cmp(&rhs_child)) + .then(self_id.cmp(&rhs_id)) + .then(published.cmp(&rhs_published)), + ) + } +} + +impl Ord for CommentResponse { + fn cmp(&self, rhs: &Self) -> cmp::Ordering { + self.partial_cmp(rhs).unwrap_or(cmp::Ordering::Equal) + } } impl Default for CommentResponse { @@ -240,43 +356,17 @@ impl CommentResponseTable { /// Parameters: /// /// `level`: comment path level for sorting comparison - pub fn sort_comments(&mut self, level: usize) { - let max_len = self - .items + pub fn sort_comments(&mut self) { + self.items.sort(); + /* + let max_len = self.items .iter() .map(|l| l.comment.path.split('.').count()) .max() .unwrap_or(0); - if level < max_len { - self.items.sort_by(|cr, cs| { - let cr_ids: Vec = cr - .comment - .path - .split('.') - .map(|p| p.parse::().unwrap_or(0)) - .collect(); - - let cs_ids: Vec = cs - .comment - .path - .split('.') - .map(|p| p.parse::().unwrap_or(0)) - .collect(); - - if level < cr_ids.len() && level < cs_ids.len() { - let cr_id = &cr_ids[level]; - let cs_id = &cs_ids[level]; - - cr_id.cmp(cs_id).then(cr_ids.len().cmp(&cs_ids.len())) - } else { - let min_level = std::cmp::min(cr_ids.len() - 1, cs_ids.len() - 1); - cr_ids[min_level].cmp(&cs_ids[min_level]) - } - }); - - self.sort_comments(level + 1); - } + (0..=max_len).for_each(|_| self.items.sort()); + */ } } @@ -375,14 +465,6 @@ mod tests { }, ..Default::default() }, - CommentResponse { - comment: Comment { - path: "0.1396433.1402606.1437059.1463746".into(), - published: "2023-08-03T11:34:11.084929".into(), - ..Default::default() - }, - ..Default::default() - }, CommentResponse { comment: Comment { path: "0.1402429.1436014.1463371".into(), @@ -396,32 +478,24 @@ mod tests { let exp_comments = vec![ CommentResponse { comment: Comment { - path: "0.1396433.1402606.1437059.1463746".into(), - published: "2023-08-03T11:34:11.084929".into(), - ..Default::default() - }, - ..Default::default() - }, - CommentResponse { - comment: Comment { - path: "0.1451065.1456841.1461024".into(), - published: "2023-08-03T08:51:59.051645".into(), + path: "0.1402429.1436014.1463371".into(), + published: "2023-08-03T11:14:25.780911".into(), ..Default::default() }, ..Default::default() }, CommentResponse { comment: Comment { - path: "0.1402429.1436014.1463371".into(), - published: "2023-08-03T11:14:25.780911".into(), + path: "0.1402429.1436014.1492422".into(), + published: "2023-08-04T06:23:05.577465".into(), ..Default::default() }, ..Default::default() }, CommentResponse { comment: Comment { - path: "0.1402429.1436014.1492422".into(), - published: "2023-08-04T06:23:05.577465".into(), + path: "0.1451065.1456841.1461024".into(), + published: "2023-08-03T08:51:59.051645".into(), ..Default::default() }, ..Default::default() @@ -477,8 +551,19 @@ mod tests { ]; let mut comment_responses = CommentResponseTable::new(comments); - comment_responses.sort_comments(1); + comment_responses.sort_comments(); + + let response_paths: Vec = comment_responses + .items() + .iter() + .map(|c| c.comment.path.clone()) + .collect(); + + let exp_paths: Vec = exp_comments + .iter() + .map(|c| c.comment.path.clone()) + .collect(); - assert_eq!(comment_responses.items(), exp_comments.as_slice()); + assert_eq!(response_paths, exp_paths); } } diff --git a/src/comments/comment.rs b/src/comments/comment.rs index 9253f96..35bafcc 100644 --- a/src/comments/comment.rs +++ b/src/comments/comment.rs @@ -3,7 +3,7 @@ use tui::widgets::ListState; /// Represents a comment on a [Post](crate::posts::Post). -#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)] pub struct Comment { pub id: u64, pub creator_id: u64, diff --git a/src/community.rs b/src/community.rs index 47542ba..8944c6a 100644 --- a/src/community.rs +++ b/src/community.rs @@ -1,7 +1,7 @@ use tui::widgets::ListState; /// Represents a response to an API request that presents a `community` field. -#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)] pub struct Community { pub id: u64, pub name: String, diff --git a/src/counts.rs b/src/counts.rs index 270b3e5..22e1078 100644 --- a/src/counts.rs +++ b/src/counts.rs @@ -4,7 +4,7 @@ use tui::widgets::ListState; /// Represents the count statistics for a [Post](crate::posts::Post), /// [Comment](crate::comment::Comment), etc. -#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)] pub struct Counts { pub id: Option, pub post_id: Option, diff --git a/src/posts.rs b/src/posts.rs index 21e097f..2b0239f 100644 --- a/src/posts.rs +++ b/src/posts.rs @@ -102,7 +102,7 @@ pub fn load_posts(file_name: &str) -> Result { } /// Represents a response from the [Post endpoint](crate::endpoint::Endpoint). -#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)] pub struct PostResponse { pub post: Post, pub creator: Creator, @@ -110,7 +110,7 @@ pub struct PostResponse { } /// Represents a list of responses to the [Post endpoint](crate::endpoint::Endpoint). -#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)] pub struct PostResponses { pub posts: Vec, } diff --git a/src/posts/creator.rs b/src/posts/creator.rs index c999090..17a557a 100644 --- a/src/posts/creator.rs +++ b/src/posts/creator.rs @@ -1,7 +1,7 @@ use tui::widgets::ListState; /// Represents a post creator as returned in a posts API response. -#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)] pub struct Creator { pub id: u64, pub name: String, diff --git a/src/posts/post.rs b/src/posts/post.rs index 1abd4da..f891301 100644 --- a/src/posts/post.rs +++ b/src/posts/post.rs @@ -1,7 +1,7 @@ use tui::widgets::ListState; /// Represents a post as returned in a posts API response. -#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)] pub struct Post { pub id: u64, pub name: String, diff --git a/src/screen/post.rs b/src/screen/post.rs index 9cff283..6d0ef06 100644 --- a/src/screen/post.rs +++ b/src/screen/post.rs @@ -101,7 +101,7 @@ pub fn draw_post_screen( if let Some(c) = app.comments.get_mut(&p.post.id()) { // sort comments chronologically, grouping by parent-child relation if !p.post.sorted() { - c.sort_comments(1); + c.sort_comments(); p.post.set_sorted(true); } @@ -226,6 +226,7 @@ fn filter_line<'l>(raw: &'l str, width: usize) -> impl Iterator + let count = wf.chars().count(); if line_len + count >= width { + line = format!("{line} {wf}"); break; } else { line_len += count;