use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EpgProgramme {
pub channel: String,
pub start: Option<i64>,
pub stop: Option<i64>,
#[serde(default)]
pub title: SmallVec<[EpgStringWithLang; 1]>,
#[serde(default)]
pub sub_title: SmallVec<[EpgStringWithLang; 1]>,
#[serde(default)]
pub desc: SmallVec<[EpgStringWithLang; 1]>,
#[serde(default)]
pub category: SmallVec<[EpgStringWithLang; 2]>,
pub credits: Option<EpgCredits>,
pub date: Option<String>,
pub length: Option<u32>,
#[serde(default)]
pub episode_num: SmallVec<[EpgEpisodeNumber; 1]>,
#[serde(default)]
pub image: SmallVec<[EpgImage; 1]>,
pub icon: Option<EpgIcon>,
#[serde(default)]
pub rating: SmallVec<[EpgRating; 1]>,
#[serde(default)]
pub star_rating: SmallVec<[EpgRating; 1]>,
#[serde(default)]
pub is_new: bool,
#[serde(default)]
pub is_premiere: bool,
#[serde(default)]
pub is_rerun: bool,
#[serde(default)]
pub is_last_chance: bool,
#[serde(default)]
pub keyword: SmallVec<[EpgStringWithLang; 2]>,
pub orig_language: Option<EpgStringWithLang>,
pub video: Option<EpgVideo>,
#[serde(default)]
pub audio: SmallVec<[EpgAudio; 1]>,
#[serde(default)]
pub review: SmallVec<[EpgReview; 1]>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EpgStringWithLang {
pub value: String,
pub lang: Option<String>,
}
impl EpgStringWithLang {
pub fn new(value: impl Into<String>) -> Self {
Self {
value: value.into(),
lang: None,
}
}
pub fn with_lang(value: impl Into<String>, lang: impl Into<String>) -> Self {
Self {
value: value.into(),
lang: Some(lang.into()),
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EpgCredits {
#[serde(default)]
pub director: SmallVec<[String; 2]>,
#[serde(default)]
pub actor: SmallVec<[EpgPerson; 4]>,
#[serde(default)]
pub writer: SmallVec<[String; 2]>,
#[serde(default)]
pub producer: SmallVec<[String; 1]>,
#[serde(default)]
pub composer: SmallVec<[String; 1]>,
#[serde(default)]
pub presenter: SmallVec<[String; 2]>,
#[serde(default)]
pub commentator: SmallVec<[String; 1]>,
#[serde(default)]
pub guest: SmallVec<[EpgPerson; 2]>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EpgPerson {
pub name: String,
pub role: Option<String>,
#[serde(default)]
pub guest: bool,
pub image: Option<String>,
pub url: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EpgEpisodeNumber {
pub value: String,
pub system: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EpgImage {
pub url: String,
pub image_type: Option<String>,
pub size: Option<String>,
pub orient: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EpgIcon {
pub src: String,
pub width: Option<u32>,
pub height: Option<u32>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EpgRating {
pub value: String,
pub system: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EpgVideo {
pub present: Option<bool>,
pub colour: Option<bool>,
pub aspect: Option<String>,
pub quality: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EpgAudio {
pub present: Option<bool>,
pub stereo: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EpgReview {
pub value: String,
pub review_type: Option<String>,
pub source: Option<String>,
pub reviewer: Option<String>,
pub lang: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EpgChannel {
pub id: String,
#[serde(default)]
pub display_name: SmallVec<[EpgStringWithLang; 1]>,
pub icon: Option<EpgIcon>,
pub url: Option<String>,
#[serde(default)]
pub icons: SmallVec<[EpgIcon; 1]>,
#[serde(default)]
pub urls: SmallVec<[String; 1]>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn programme_default_has_empty_title() {
let p = EpgProgramme::default();
assert!(p.title.is_empty());
assert!(p.channel.is_empty());
assert!(!p.is_new);
}
#[test]
fn programme_default_has_empty_new_fields() {
let p = EpgProgramme::default();
assert!(p.keyword.is_empty());
assert!(p.orig_language.is_none());
assert!(p.video.is_none());
assert!(p.audio.is_empty());
assert!(p.review.is_empty());
}
#[test]
fn epg_video_default_has_all_none() {
let v = EpgVideo::default();
assert!(v.present.is_none());
assert!(v.colour.is_none());
assert!(v.aspect.is_none());
assert!(v.quality.is_none());
}
#[test]
fn string_with_lang_constructors() {
let plain = EpgStringWithLang::new("Hello");
assert_eq!(plain.value, "Hello");
assert!(plain.lang.is_none());
let lang = EpgStringWithLang::with_lang("Bonjour", "fr");
assert_eq!(lang.value, "Bonjour");
assert_eq!(lang.lang.as_deref(), Some("fr"));
}
}