From a311e2b89563e6fd6b4fde9856b0ad6efed78748 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Thu, 3 Sep 2026 13:20:21 -0600 Subject: [PATCH] fix(readarr): stop appending stray colons to the edition overview Every line of the edition details popup was parsed as a label and a value, so a line carrying no colon had one appended to it. A multi-line overview therefore rendered with a spurious colon at the end of each continuation line, and Goodreads sourced descriptions, which use CRLF, turned their blank lines into a lone colon. Split each line on the first colon only, leave a line without one unlabelled, and trim the trailing carriage return so a CRLF blank line renders as a real paragraph break. Building the line is now a named function so its branches can be asserted directly. Rendered text discards the carriage return, so a snapshot alone cannot tell the trimming apart from leaving it in place. A continuation line that happens to contain a colon is still styled as a label. Telling those apart needs the details to be built as styled lines rather than flattened to text and parsed back. --- .../servarr_data/readarr/readarr_data.rs | 26 ++++---- .../readarr_ui/library/edition_details_ui.rs | 25 ++++---- .../library/edition_details_ui_tests.rs | 63 ++++++++++++++++++- ...ers_edition_details_over_book_details.snap | 6 +- ...hot_tests__edition_details_ui_renders.snap | 6 +- ...__edition_details_ui_renders_scrolled.snap | 6 +- ...details_ui_renders_with_zero_editions.snap | 6 +- 7 files changed, 103 insertions(+), 35 deletions(-) diff --git a/src/models/servarr_data/readarr/readarr_data.rs b/src/models/servarr_data/readarr/readarr_data.rs index 3a1cb8ef..30ad8344 100644 --- a/src/models/servarr_data/readarr/readarr_data.rs +++ b/src/models/servarr_data/readarr/readarr_data.rs @@ -448,17 +448,21 @@ impl ReadarrData<'_> { let edition_details_modal = EditionDetailsModal { edition_details: ScrollableText::with_string( - "Title: Test Edition -Format: Hardcover -Language: English -Publisher: DAW Books -Page Count: 288 -Release Date: 2023-05-20 00:00:00 UTC -ISBN13: 9780756404079 -ASIN: B0043RSJ9S -Ratings: 4.5 (1000 votes) -Monitored: true -Overview: A test edition overview." + "Title: Test Edition\n\ + Format: Hardcover\n\ + Language: English\n\ + Publisher: DAW Books\n\ + Page Count: 288\n\ + Release Date: 2023-05-20 00:00:00 UTC\n\ + ISBN13: 9780756404079\n\ + ASIN: B0043RSJ9S\n\ + Ratings: 4.5 (1000 votes)\n\ + Monitored: true\n\ + Overview: original cover of ISBN 075640407X\r\n\ + \r\n\ + My name is Kvothe, pronounced nearly the same as \"quothe.\"\r\n\ + \r\n\ + The Adem call me Maedre: which means The Flame.\r\n" .to_owned(), ), }; diff --git a/src/ui/readarr_ui/library/edition_details_ui.rs b/src/ui/readarr_ui/library/edition_details_ui.rs index 811bd1e6..8f4d5c0c 100644 --- a/src/ui/readarr_ui/library/edition_details_ui.rs +++ b/src/ui/readarr_ui/library/edition_details_ui.rs @@ -56,17 +56,8 @@ fn draw_edition_details(f: &mut Frame<'_>, app: &App<'_>, area: Rect) { edition_details .items .iter() - .filter(|line| !line.is_empty()) - .map(|line| { - let split = line.split(':').collect::>(); - let title = format!("{}:", split[0]); - - Line::from(vec![ - title.bold().style(style), - Span::styled(split[1..].join(":"), style), - ]) - }) - .collect::>>(), + .map(|line| edition_detail_line(line, style)) + .collect::>>(), ); let paragraph = Paragraph::new(text) @@ -100,3 +91,15 @@ fn style_from_edition(edition: &Edition) -> Style { primary_style() } + +fn edition_detail_line(line: &str, style: Style) -> Line<'static> { + let line = line.trim_end(); + + match line.split_once(':') { + Some((label, value)) => Line::from(vec![ + format!("{label}:").bold().style(style), + Span::styled(value.to_owned(), style), + ]), + None => Line::from(Span::styled(line.to_owned(), style)), + } +} diff --git a/src/ui/readarr_ui/library/edition_details_ui_tests.rs b/src/ui/readarr_ui/library/edition_details_ui_tests.rs index 7d7c6add..8d9a94d5 100644 --- a/src/ui/readarr_ui/library/edition_details_ui_tests.rs +++ b/src/ui/readarr_ui/library/edition_details_ui_tests.rs @@ -1,6 +1,7 @@ #[cfg(test)] mod tests { use pretty_assertions::assert_eq; + use ratatui::text::Line; use strum::IntoEnumIterator; use crate::app::App; @@ -9,7 +10,9 @@ mod tests { ActiveReadarrBlock, EDITION_DETAILS_BLOCKS, }; use crate::ui::DrawUi; - use crate::ui::readarr_ui::library::edition_details_ui::{EditionDetailsUi, style_from_edition}; + use crate::ui::readarr_ui::library::edition_details_ui::{ + EditionDetailsUi, edition_detail_line, style_from_edition, + }; use crate::ui::styles::{primary_style, unmonitored_style}; use crate::ui::ui_test_utils::test_utils::render_to_string_with_app; @@ -48,6 +51,64 @@ mod tests { assert_eq!(style, unmonitored_style()); } + #[test] + fn test_edition_detail_line_splits_a_label_from_its_value() { + let line = edition_detail_line("Title: Test Edition", primary_style()); + + assert_eq!(span_contents(&line), vec!["Title:", " Test Edition"]); + } + + #[test] + fn test_edition_detail_line_keeps_colons_within_a_value() { + let line = edition_detail_line("Release Date: 2023-01-05 15:00:00 UTC", primary_style()); + + assert_eq!( + span_contents(&line), + vec!["Release Date:", " 2023-01-05 15:00:00 UTC"] + ); + } + + #[test] + fn test_edition_detail_line_does_not_append_a_colon_to_a_line_without_one() { + let line = edition_detail_line( + "My name is Kvothe, pronounced nearly the same as \"quothe.\"", + primary_style(), + ); + + assert_eq!( + span_contents(&line), + vec!["My name is Kvothe, pronounced nearly the same as \"quothe.\""] + ); + } + + #[test] + fn test_edition_detail_line_renders_a_carriage_return_only_line_as_blank() { + let line = edition_detail_line("\r", primary_style()); + + assert_eq!(span_contents(&line), vec![""]); + } + + #[test] + fn test_edition_detail_line_trims_a_trailing_carriage_return() { + let line = edition_detail_line( + "I have stolen princesses back from sleeping barrow kings.\r", + primary_style(), + ); + + assert_eq!( + span_contents(&line), + vec!["I have stolen princesses back from sleeping barrow kings."] + ); + } + + fn span_contents(line: &Line<'_>) -> Vec { + line + .spans + .iter() + .map(|span| span.content.to_string()) + .collect() + } + mod snapshot_tests { use crate::models::servarr_data::readarr::modals::EditionDetailsModal; use crate::models::stateful_table::StatefulTable; diff --git a/src/ui/readarr_ui/library/snapshots/managarr__ui__readarr_ui__library__book_details_ui__book_details_ui_tests__tests__snapshot_tests__book_details_ui_renders_edition_details_over_book_details.snap b/src/ui/readarr_ui/library/snapshots/managarr__ui__readarr_ui__library__book_details_ui__book_details_ui_tests__tests__snapshot_tests__book_details_ui_renders_edition_details_over_book_details.snap index 2c0295dd..b675a77a 100644 --- a/src/ui/readarr_ui/library/snapshots/managarr__ui__readarr_ui__library__book_details_ui__book_details_ui_tests__tests__snapshot_tests__book_details_ui_renders_edition_details_over_book_details.snap +++ b/src/ui/readarr_ui/library/snapshots/managarr__ui__readarr_ui__library__book_details_ui__book_details_ui_tests__tests__snapshot_tests__book_details_ui_renders_edition_details_over_book_details.snap @@ -19,11 +19,11 @@ expression: output │ │ASIN: B0043RSJ9S │ │ │ │Ratings: 4.5 (1000 votes) │ │ │ │Monitored: true │ │ - │ │Overview: A test edition overview. │ │ - │ │ │ │ - │ │ │ │ + │ │Overview: original cover of ISBN 075640407X │ │ │ │ │ │ + │ │My name is Kvothe, pronounced nearly the same as "quothe." │ │ │ │ │ │ + │ │The Adem call me Maedre: which means The Flame. │ │ │ │ │ │ │ │ │ │ │ │ │ │ diff --git a/src/ui/readarr_ui/library/snapshots/managarr__ui__readarr_ui__library__edition_details_ui__edition_details_ui_tests__tests__snapshot_tests__edition_details_ui_renders.snap b/src/ui/readarr_ui/library/snapshots/managarr__ui__readarr_ui__library__edition_details_ui__edition_details_ui_tests__tests__snapshot_tests__edition_details_ui_renders.snap index 1e44dfb2..a8f37c38 100644 --- a/src/ui/readarr_ui/library/snapshots/managarr__ui__readarr_ui__library__edition_details_ui__edition_details_ui_tests__tests__snapshot_tests__edition_details_ui_renders.snap +++ b/src/ui/readarr_ui/library/snapshots/managarr__ui__readarr_ui__library__edition_details_ui__edition_details_ui_tests__tests__snapshot_tests__edition_details_ui_renders.snap @@ -19,11 +19,11 @@ expression: output │ASIN: B0043RSJ9S │ │Ratings: 4.5 (1000 votes) │ │Monitored: true │ - │Overview: A test edition overview. │ - │ │ - │ │ + │Overview: original cover of ISBN 075640407X │ │ │ + │My name is Kvothe, pronounced nearly the same as "quothe." │ │ │ + │The Adem call me Maedre: which means The Flame. │ │ │ │ │ │ │ diff --git a/src/ui/readarr_ui/library/snapshots/managarr__ui__readarr_ui__library__edition_details_ui__edition_details_ui_tests__tests__snapshot_tests__edition_details_ui_renders_scrolled.snap b/src/ui/readarr_ui/library/snapshots/managarr__ui__readarr_ui__library__edition_details_ui__edition_details_ui_tests__tests__snapshot_tests__edition_details_ui_renders_scrolled.snap index 94901d82..38e4b079 100644 --- a/src/ui/readarr_ui/library/snapshots/managarr__ui__readarr_ui__library__edition_details_ui__edition_details_ui_tests__tests__snapshot_tests__edition_details_ui_renders_scrolled.snap +++ b/src/ui/readarr_ui/library/snapshots/managarr__ui__readarr_ui__library__edition_details_ui__edition_details_ui_tests__tests__snapshot_tests__edition_details_ui_renders_scrolled.snap @@ -17,11 +17,11 @@ expression: output │ASIN: B0043RSJ9S │ │Ratings: 4.5 (1000 votes) │ │Monitored: true │ - │Overview: A test edition overview. │ - │ │ - │ │ + │Overview: original cover of ISBN 075640407X │ │ │ + │My name is Kvothe, pronounced nearly the same as "quothe." │ │ │ + │The Adem call me Maedre: which means The Flame. │ │ │ │ │ │ │ diff --git a/src/ui/readarr_ui/library/snapshots/managarr__ui__readarr_ui__library__edition_details_ui__edition_details_ui_tests__tests__snapshot_tests__edition_details_ui_renders_with_zero_editions.snap b/src/ui/readarr_ui/library/snapshots/managarr__ui__readarr_ui__library__edition_details_ui__edition_details_ui_tests__tests__snapshot_tests__edition_details_ui_renders_with_zero_editions.snap index 1e44dfb2..a8f37c38 100644 --- a/src/ui/readarr_ui/library/snapshots/managarr__ui__readarr_ui__library__edition_details_ui__edition_details_ui_tests__tests__snapshot_tests__edition_details_ui_renders_with_zero_editions.snap +++ b/src/ui/readarr_ui/library/snapshots/managarr__ui__readarr_ui__library__edition_details_ui__edition_details_ui_tests__tests__snapshot_tests__edition_details_ui_renders_with_zero_editions.snap @@ -19,11 +19,11 @@ expression: output │ASIN: B0043RSJ9S │ │Ratings: 4.5 (1000 votes) │ │Monitored: true │ - │Overview: A test edition overview. │ - │ │ - │ │ + │Overview: original cover of ISBN 075640407X │ │ │ + │My name is Kvothe, pronounced nearly the same as "quothe." │ │ │ + │The Adem call me Maedre: which means The Flame. │ │ │ │ │ │ │