/// Trims a feed URL entered by the user, returning `None` when it's blank /// so the submit handler can bail out before making a request. pub fn normalize_feed_url(input: &str) -> Option { let trimmed = input.trim(); if trimmed.is_empty() { None } else { Some(trimmed.to_string()) } } /// Success message shown after a feed subscription pulls in new articles. fn subscribed_message(article_count: usize) -> String { format!("Subscribed — pulled in {article_count} article(s).") } /// What the form should show, and whether the URL input should be cleared, /// after a subscription attempt resolves. #[derive(Debug, Clone, PartialEq, Eq)] pub enum SubmitOutcome { /// The subscription succeeded: show `message` and clear the input. Success { message: String }, /// The subscription failed: show `message` and leave the input as-is, /// so the reader can fix it up and retry. Failure { message: String }, } /// Maps a `subscribe_feed` result to what the form should do next. pub fn resolve_submit_outcome(result: Result) -> SubmitOutcome { match result { Ok(count) => SubmitOutcome::Success { message: subscribed_message(count), }, Err(message) => SubmitOutcome::Failure { message }, } } #[cfg(test)] mod tests { use super::*; #[test] fn blank_input_normalizes_to_none() { assert_eq!(normalize_feed_url(""), None); assert_eq!(normalize_feed_url(" "), None); } #[test] fn surrounding_whitespace_is_trimmed() { assert_eq!( normalize_feed_url(" https://example.com/feed.xml "), Some("https://example.com/feed.xml".to_string()) ); } #[test] fn success_reports_the_article_count_and_clears_the_input() { assert_eq!( resolve_submit_outcome(Ok(3)), SubmitOutcome::Success { message: "Subscribed — pulled in 3 article(s).".to_string() } ); } /// A failed attempt should surface the error but leave the input alone /// so the reader doesn't have to retype the URL to fix it. #[test] fn failure_surfaces_the_error_message() { assert_eq!( resolve_submit_outcome(Err("feed unreachable".to_string())), SubmitOutcome::Failure { message: "feed unreachable".to_string() } ); } }