feat: add title param to render_feed, use dynamic title in RSS adapter

This commit is contained in:
2026-05-04 20:52:07 +02:00
parent 49b79799c1
commit b5a8ea2395
4 changed files with 29 additions and 10 deletions

View File

@@ -14,11 +14,11 @@ impl RssAdapter {
} }
impl RssFeedRenderer for RssAdapter { impl RssFeedRenderer for RssAdapter {
fn render_feed(&self, entries: &[DiaryEntry]) -> Result<String, String> { fn render_feed(&self, entries: &[DiaryEntry], title: &str) -> Result<String, String> {
let items = entries let items = entries
.iter() .iter()
.map(|e| { .map(|e| {
let title = format!( let item_title = format!(
"{} ({})", "{} ({})",
e.movie().title().value(), e.movie().title().value(),
e.movie().release_year().value() e.movie().release_year().value()
@@ -38,7 +38,7 @@ impl RssFeedRenderer for RssAdapter {
.permalink(false) .permalink(false)
.build(); .build();
ItemBuilder::default() ItemBuilder::default()
.title(Some(title)) .title(Some(item_title))
.description(Some(description)) .description(Some(description))
.pub_date(Some(pub_date)) .pub_date(Some(pub_date))
.guid(Some(guid)) .guid(Some(guid))
@@ -47,12 +47,31 @@ impl RssFeedRenderer for RssAdapter {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let channel = ChannelBuilder::default() let channel = ChannelBuilder::default()
.title(self.feed_title.clone()) .title(title.to_string())
.link(self.feed_link.clone()) .link(self.feed_link.clone())
.description(self.feed_title.clone()) .description(title.to_string())
.items(items) .items(items)
.build(); .build();
Ok(channel.to_string()) Ok(channel.to_string())
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn render_feed_uses_provided_title() {
let adapter = RssAdapter::new("ignored".into(), "http://example.com".into());
let xml = adapter.render_feed(&[], "Custom Title").unwrap();
assert!(xml.contains("<title>Custom Title</title>"));
}
#[test]
fn render_feed_empty_entries_produces_valid_xml() {
let adapter = RssAdapter::new("ignored".into(), "http://example.com".into());
let xml = adapter.render_feed(&[], "My Feed").unwrap();
assert!(xml.starts_with("<?xml") || xml.starts_with("<rss"));
}
}

View File

@@ -67,5 +67,5 @@ pub trait HtmlRenderer: Send + Sync {
} }
pub trait RssFeedRenderer: Send + Sync { pub trait RssFeedRenderer: Send + Sync {
fn render_feed(&self, entries: &[DiaryEntry]) -> Result<String, String>; fn render_feed(&self, entries: &[DiaryEntry], title: &str) -> Result<String, String>;
} }

View File

@@ -153,7 +153,7 @@ mod tests {
struct PanicRssRenderer; struct PanicRssRenderer;
impl crate::ports::RssFeedRenderer for PanicRssRenderer { impl crate::ports::RssFeedRenderer for PanicRssRenderer {
fn render_feed(&self, _: &[domain::models::DiaryEntry]) -> Result<String, String> { panic!() } fn render_feed(&self, _: &[domain::models::DiaryEntry], _: &str) -> Result<String, String> { panic!() }
} }
struct PanicMeta; struct PanicFetcher; struct PanicStorage; struct PanicEvent; struct PanicHasher; struct PanicAuth; struct PanicUserRepo; struct PanicMeta; struct PanicFetcher; struct PanicStorage; struct PanicEvent; struct PanicHasher; struct PanicAuth; struct PanicUserRepo;
@@ -269,7 +269,7 @@ mod tests {
} }
struct PanicRssRenderer2; struct PanicRssRenderer2;
impl crate::ports::RssFeedRenderer for PanicRssRenderer2 { impl crate::ports::RssFeedRenderer for PanicRssRenderer2 {
fn render_feed(&self, _: &[domain::models::DiaryEntry]) -> Result<String, String> { panic!() } fn render_feed(&self, _: &[domain::models::DiaryEntry], _: &str) -> Result<String, String> { panic!() }
} }
struct PanicAuth2; struct PanicAuth2;
crate::state::AppState { crate::state::AppState {
@@ -329,7 +329,7 @@ mod tests {
} }
struct PanicRssRenderer3; struct PanicRssRenderer3;
impl crate::ports::RssFeedRenderer for PanicRssRenderer3 { impl crate::ports::RssFeedRenderer for PanicRssRenderer3 {
fn render_feed(&self, _: &[domain::models::DiaryEntry]) -> Result<String, String> { panic!() } fn render_feed(&self, _: &[domain::models::DiaryEntry], _: &str) -> Result<String, String> { panic!() }
} }
crate::state::AppState { crate::state::AppState {
app_ctx: AppContext { app_ctx: AppContext {

View File

@@ -380,7 +380,7 @@ pub mod rss {
let page = get_diary::execute(&state.app_ctx, query).await?; let page = get_diary::execute(&state.app_ctx, query).await?;
let xml = state let xml = state
.rss_renderer .rss_renderer
.render_feed(&page.items) .render_feed(&page.items, "Movie Diary")
.map_err(|e| ApiError(DomainError::InfrastructureError(e)))?; .map_err(|e| ApiError(DomainError::InfrastructureError(e)))?;
Ok(([(header::CONTENT_TYPE, "application/rss+xml; charset=utf-8")], xml)) Ok(([(header::CONTENT_TYPE, "application/rss+xml; charset=utf-8")], xml))
} }