use crate::types::{Enclosure, Entry, FeedMeta, MediaRating, Tag};
pub const MEDIA_NAMESPACE: &str = "http://search.yahoo.com/mrss/";
#[derive(Debug, Clone, Default, PartialEq)]
#[allow(clippy::derive_partial_eq_without_eq)]
pub struct MediaContent {
pub url: String,
pub type_: Option<String>,
pub medium: Option<String>,
pub file_size: Option<u64>,
pub bitrate: Option<u32>,
pub framerate: Option<f32>,
pub width: Option<u32>,
pub height: Option<u32>,
pub duration: Option<u32>,
pub expression: Option<String>,
pub is_default: Option<bool>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct MediaThumbnail {
pub url: String,
pub width: Option<u32>,
pub height: Option<u32>,
pub time: Option<String>,
}
fn parse_rating(scheme: Option<&str>, text: &str) -> Option<MediaRating> {
let content = text.trim().to_string();
if content.is_empty() {
return None;
}
Some(MediaRating {
scheme: scheme.map(str::to_owned),
content,
})
}
pub fn handle_feed_element(element: &str, scheme: Option<&str>, text: &str, feed: &mut FeedMeta) {
match element {
"rating" if feed.media_rating.is_none() => {
feed.media_rating = parse_rating(scheme, text);
}
"keywords" if feed.media_keywords.is_none() && !text.trim().is_empty() => {
feed.media_keywords = Some(text.trim().to_string());
}
_ => {}
}
}
pub fn handle_entry_element(element: &str, text: &str, entry: &mut Entry) {
match element {
"title" if entry.title.is_none() => {
entry.title = Some(text.to_string());
}
"description" if entry.summary.is_none() => {
entry.summary = Some(text.to_string());
}
"keywords" => {
if entry.media_keywords.is_none() && !text.is_empty() {
entry.media_keywords = Some(text.to_string());
}
for keyword in text.split(',') {
let keyword = keyword.trim();
if !keyword.is_empty() {
entry.tags.push(Tag::new(keyword));
}
}
}
"category" if !text.is_empty() => {
entry.tags.push(Tag::new(text));
}
_ => {
}
}
}
pub fn handle_entry_rating(scheme: Option<&str>, text: &str, entry: &mut Entry) {
if entry.media_rating.is_none() {
entry.media_rating = parse_rating(scheme, text);
}
}
pub fn media_content_to_enclosure(content: &MediaContent) -> Enclosure {
Enclosure {
url: content.url.clone().into(),
enclosure_type: content.type_.as_ref().map(|t| t.clone().into()),
length: content.file_size.map(|v| v.to_string()),
title: None,
duration: None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_media_title() {
let mut entry = Entry::default();
handle_entry_element("title", "Video Title", &mut entry);
assert_eq!(entry.title.as_deref(), Some("Video Title"));
}
#[test]
fn test_media_description() {
let mut entry = Entry::default();
handle_entry_element("description", "Video description", &mut entry);
assert_eq!(entry.summary.as_deref(), Some("Video description"));
}
#[test]
fn test_media_keywords() {
let mut entry = Entry::default();
handle_entry_element("keywords", "tech, programming, rust", &mut entry);
assert_eq!(entry.tags.len(), 3);
assert_eq!(entry.tags[0].term, "tech");
assert_eq!(entry.tags[1].term, "programming");
assert_eq!(entry.tags[2].term, "rust");
}
#[test]
fn test_media_keywords_with_spaces() {
let mut entry = Entry::default();
handle_entry_element("keywords", " tech , programming ", &mut entry);
assert_eq!(entry.tags.len(), 2);
assert_eq!(entry.tags[0].term, "tech");
assert_eq!(entry.tags[1].term, "programming");
}
#[test]
fn test_media_category() {
let mut entry = Entry::default();
handle_entry_element("category", "Technology", &mut entry);
assert_eq!(entry.tags.len(), 1);
assert_eq!(entry.tags[0].term, "Technology");
}
#[test]
fn test_media_content_default() {
let content = MediaContent::default();
assert!(content.url.is_empty());
assert!(content.type_.is_none());
assert!(content.medium.is_none());
assert!(content.file_size.is_none());
assert!(content.bitrate.is_none());
assert!(content.framerate.is_none());
assert!(content.width.is_none());
assert!(content.height.is_none());
assert!(content.duration.is_none());
assert!(content.expression.is_none());
assert!(content.is_default.is_none());
}
#[test]
fn test_media_content_full_attributes() {
let content = MediaContent {
url: "https://example.com/video.mp4".to_string(),
type_: Some("video/mp4".to_string()),
medium: Some("video".to_string()),
file_size: Some(10_485_760), bitrate: Some(1500), framerate: Some(30.0),
width: Some(1920),
height: Some(1080),
duration: Some(600), expression: Some("full".to_string()),
is_default: Some(true),
};
assert_eq!(content.url, "https://example.com/video.mp4");
assert_eq!(content.type_.as_deref(), Some("video/mp4"));
assert_eq!(content.medium.as_deref(), Some("video"));
assert_eq!(content.file_size, Some(10_485_760));
assert_eq!(content.bitrate, Some(1500));
assert_eq!(content.framerate, Some(30.0));
assert_eq!(content.width, Some(1920));
assert_eq!(content.height, Some(1080));
assert_eq!(content.duration, Some(600));
assert_eq!(content.expression.as_deref(), Some("full"));
assert_eq!(content.is_default, Some(true));
}
#[test]
fn test_media_content_audio() {
let content = MediaContent {
url: "https://example.com/audio.mp3".to_string(),
type_: Some("audio/mpeg".to_string()),
medium: Some("audio".to_string()),
file_size: Some(5_242_880), bitrate: Some(128), duration: Some(180), ..Default::default()
};
assert_eq!(content.medium.as_deref(), Some("audio"));
assert_eq!(content.bitrate, Some(128));
assert!(content.width.is_none());
assert!(content.height.is_none());
assert!(content.framerate.is_none());
}
#[test]
fn test_media_content_image() {
let content = MediaContent {
url: "https://example.com/image.jpg".to_string(),
type_: Some("image/jpeg".to_string()),
medium: Some("image".to_string()),
width: Some(800),
height: Some(600),
..Default::default()
};
assert_eq!(content.medium.as_deref(), Some("image"));
assert_eq!(content.width, Some(800));
assert_eq!(content.height, Some(600));
assert!(content.duration.is_none());
}
#[test]
fn test_media_content_expression_variants() {
let full = MediaContent {
expression: Some("full".to_string()),
..Default::default()
};
let sample = MediaContent {
expression: Some("sample".to_string()),
..Default::default()
};
let nonstop = MediaContent {
expression: Some("nonstop".to_string()),
..Default::default()
};
assert_eq!(full.expression.as_deref(), Some("full"));
assert_eq!(sample.expression.as_deref(), Some("sample"));
assert_eq!(nonstop.expression.as_deref(), Some("nonstop"));
}
#[test]
fn test_media_thumbnail_default() {
let thumbnail = MediaThumbnail::default();
assert!(thumbnail.url.is_empty());
assert!(thumbnail.width.is_none());
assert!(thumbnail.height.is_none());
assert!(thumbnail.time.is_none());
}
#[test]
fn test_media_thumbnail_full_attributes() {
let thumbnail = MediaThumbnail {
url: "https://example.com/thumb.jpg".to_string(),
width: Some(640),
height: Some(480),
time: Some("12:05:01.123".to_string()),
};
assert_eq!(thumbnail.url, "https://example.com/thumb.jpg");
assert_eq!(thumbnail.width, Some(640));
assert_eq!(thumbnail.height, Some(480));
assert_eq!(thumbnail.time.as_deref(), Some("12:05:01.123"));
}
#[test]
fn test_media_thumbnail_without_time() {
let thumbnail = MediaThumbnail {
url: "https://example.com/poster.jpg".to_string(),
width: Some(1920),
height: Some(1080),
time: None,
};
assert_eq!(thumbnail.width, Some(1920));
assert_eq!(thumbnail.height, Some(1080));
assert!(thumbnail.time.is_none());
}
#[test]
fn test_media_content_to_enclosure() {
let content = MediaContent {
url: "https://example.com/video.mp4".to_string(),
type_: Some("video/mp4".to_string()),
file_size: Some(1_024_000),
width: Some(1920), height: Some(1080),
..Default::default()
};
let enclosure = media_content_to_enclosure(&content);
assert_eq!(enclosure.url, "https://example.com/video.mp4");
assert_eq!(enclosure.enclosure_type.as_deref(), Some("video/mp4"));
assert_eq!(enclosure.length.as_deref(), Some("1024000"));
}
#[test]
fn test_media_content_to_enclosure_minimal() {
let content = MediaContent {
url: "https://example.com/file.bin".to_string(),
..Default::default()
};
let enclosure = media_content_to_enclosure(&content);
assert_eq!(enclosure.url, "https://example.com/file.bin");
assert!(enclosure.enclosure_type.is_none());
assert!(enclosure.length.is_none());
}
#[test]
fn test_empty_keywords() {
let mut entry = Entry::default();
handle_entry_element("keywords", "", &mut entry);
assert!(entry.tags.is_empty());
}
#[test]
fn test_keywords_with_empty_values() {
let mut entry = Entry::default();
handle_entry_element("keywords", "tech, , programming", &mut entry);
assert_eq!(entry.tags.len(), 2);
assert_eq!(entry.tags[0].term, "tech");
assert_eq!(entry.tags[1].term, "programming");
}
}