///|
fn category_values(categories : Array[Category]) -> Array[String] {
let result = []
for category in categories {
result.push(category.value)
}
result
}
///|
fn item_authors(author : String?) -> Array[@model.Author] {
match author {
Some(value) => [{ name: Some(value), url: None, avatar: None, }]
None => []
}
}
///|
fn item_id(item : Item, path : String) -> String raise ParseError {
match item.guid {
Some(guid) => guid.value
None =>
match item.link {
Some(link) if !link.is_empty() => link
_ =>
raise InvalidRequiredField(
path + ".id",
"guid or link is required for a stable item identity",
)
}
}
}
///|
fn attachment(enclosure : Enclosure) -> @model.Attachment {
{
url: enclosure.url,
mime_type: enclosure.mime_type,
title: None,
size_in_bytes: Some(enclosure.length),
duration_in_seconds: None,
}
}
///|
fn normalize_item(item : Item, index : Int) -> @model.FeedItem raise ParseError {
let path = "channel.item[\{index}]"
let description = item.description
let published = item.pub_date
let published_at = @datetime.parse_optional_datetime(published)
{
id: item_id(item, path),
title: item.title,
url: item.link,
external_url: None,
authors: item_authors(item.author),
summary: description,
content_text: None,
content_html: description,
published,
published_at,
updated: None,
updated_at: None,
language: None,
categories: category_values(item.categories),
attachments: match item.enclosure {
Some(value) => [attachment(value)]
None => []
},
}
}
///|
/// Convert an RSS 2.0 channel to the shared Feed model.
/// RSS descriptions are retained as both the item summary and HTML content because
/// RSS does not declare whether description text contains markup.
pub fn normalize(channel : Channel) -> @model.Feed raise ParseError {
let updated = match channel.last_build_date {
Some(value) => Some(value)
None => channel.pub_date
}
let updated_at = @datetime.parse_optional_datetime(updated)
let items = []
for index, item in channel.items {
items.push(normalize_item(item, index))
}
{
title: channel.title,
description: Some(channel.description),
home_page_url: Some(channel.link),
feed_url: None,
language: channel.language,
authors: match channel.managing_editor {
Some(value) => [{ name: Some(value), url: None, avatar: None, }]
None => []
},
categories: category_values(channel.categories),
updated,
updated_at,
items,
}
}
///|
/// Parse and normalize an RSS 2.0 document in one operation.
pub fn parse(input : String) -> @model.Feed raise ParseError {
normalize(parse_raw(input))
}