1use serde::{Deserialize, Serialize};
7use smallvec::SmallVec;
8
9#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11pub struct EpgProgramme {
12 pub channel: String,
14
15 pub start: Option<i64>,
17
18 pub stop: Option<i64>,
20
21 #[serde(default)]
23 pub title: SmallVec<[EpgStringWithLang; 1]>,
24
25 #[serde(default)]
27 pub sub_title: SmallVec<[EpgStringWithLang; 1]>,
28
29 #[serde(default)]
31 pub desc: SmallVec<[EpgStringWithLang; 1]>,
32
33 #[serde(default)]
35 pub category: SmallVec<[EpgStringWithLang; 2]>,
36
37 pub credits: Option<EpgCredits>,
39
40 pub date: Option<String>,
42
43 pub length: Option<u32>,
45
46 #[serde(default)]
48 pub episode_num: SmallVec<[EpgEpisodeNumber; 1]>,
49
50 #[serde(default)]
52 pub image: SmallVec<[EpgImage; 1]>,
53
54 pub icon: Option<EpgIcon>,
56
57 #[serde(default)]
59 pub rating: SmallVec<[EpgRating; 1]>,
60
61 #[serde(default)]
63 pub star_rating: SmallVec<[EpgRating; 1]>,
64
65 #[serde(default)]
67 pub is_new: bool,
68
69 #[serde(default)]
70 pub is_premiere: bool,
71
72 #[serde(default)]
73 pub is_rerun: bool,
74
75 #[serde(default)]
76 pub is_last_chance: bool,
77
78 #[serde(default)]
80 pub keyword: SmallVec<[EpgStringWithLang; 2]>,
81
82 pub orig_language: Option<EpgStringWithLang>,
84
85 pub video: Option<EpgVideo>,
87
88 #[serde(default)]
90 pub audio: SmallVec<[EpgAudio; 1]>,
91
92 #[serde(default)]
94 pub review: SmallVec<[EpgReview; 1]>,
95}
96
97#[derive(Debug, Clone, Default, Serialize, Deserialize)]
99pub struct EpgStringWithLang {
100 pub value: String,
101 pub lang: Option<String>,
102}
103
104impl EpgStringWithLang {
105 pub fn new(value: impl Into<String>) -> Self {
106 Self {
107 value: value.into(),
108 lang: None,
109 }
110 }
111
112 pub fn with_lang(value: impl Into<String>, lang: impl Into<String>) -> Self {
113 Self {
114 value: value.into(),
115 lang: Some(lang.into()),
116 }
117 }
118}
119
120#[derive(Debug, Clone, Default, Serialize, Deserialize)]
122pub struct EpgCredits {
123 #[serde(default)]
124 pub director: SmallVec<[String; 2]>,
125
126 #[serde(default)]
127 pub actor: SmallVec<[EpgPerson; 4]>,
128
129 #[serde(default)]
130 pub writer: SmallVec<[String; 2]>,
131
132 #[serde(default)]
133 pub producer: SmallVec<[String; 1]>,
134
135 #[serde(default)]
136 pub composer: SmallVec<[String; 1]>,
137
138 #[serde(default)]
139 pub presenter: SmallVec<[String; 2]>,
140
141 #[serde(default)]
142 pub commentator: SmallVec<[String; 1]>,
143
144 #[serde(default)]
145 pub guest: SmallVec<[EpgPerson; 2]>,
146}
147
148#[derive(Debug, Clone, Default, Serialize, Deserialize)]
150pub struct EpgPerson {
151 pub name: String,
152 pub role: Option<String>,
153 #[serde(default)]
154 pub guest: bool,
155 pub image: Option<String>,
156 pub url: Option<String>,
157}
158
159#[derive(Debug, Clone, Default, Serialize, Deserialize)]
161pub struct EpgEpisodeNumber {
162 pub value: String,
163 pub system: Option<String>,
165}
166
167#[derive(Debug, Clone, Default, Serialize, Deserialize)]
169pub struct EpgImage {
170 pub url: String,
171 pub image_type: Option<String>,
173 pub size: Option<String>,
174 pub orient: Option<String>,
175}
176
177#[derive(Debug, Clone, Default, Serialize, Deserialize)]
179pub struct EpgIcon {
180 pub src: String,
181 pub width: Option<u32>,
182 pub height: Option<u32>,
183}
184
185#[derive(Debug, Clone, Default, Serialize, Deserialize)]
187pub struct EpgRating {
188 pub value: String,
189 pub system: Option<String>,
190}
191
192#[derive(Debug, Clone, Default, Serialize, Deserialize)]
194pub struct EpgVideo {
195 pub present: Option<bool>,
196 pub colour: Option<bool>,
197 pub aspect: Option<String>,
198 pub quality: Option<String>,
199}
200
201#[derive(Debug, Clone, Default, Serialize, Deserialize)]
203pub struct EpgAudio {
204 pub present: Option<bool>,
205 pub stereo: Option<String>,
206}
207
208#[derive(Debug, Clone, Default, Serialize, Deserialize)]
210pub struct EpgReview {
211 pub value: String,
212 pub review_type: Option<String>,
213 pub source: Option<String>,
214 pub reviewer: Option<String>,
215 pub lang: Option<String>,
216}
217
218#[derive(Debug, Clone, Default, Serialize, Deserialize)]
220pub struct EpgChannel {
221 pub id: String,
222 #[serde(default)]
223 pub display_name: SmallVec<[EpgStringWithLang; 1]>,
224 pub icon: Option<EpgIcon>,
226 pub url: Option<String>,
228 #[serde(default)]
230 pub icons: SmallVec<[EpgIcon; 1]>,
231 #[serde(default)]
233 pub urls: SmallVec<[String; 1]>,
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239
240 #[test]
241 fn programme_default_has_empty_title() {
242 let p = EpgProgramme::default();
243 assert!(p.title.is_empty());
244 assert!(p.channel.is_empty());
245 assert!(!p.is_new);
246 }
247
248 #[test]
249 fn programme_default_has_empty_new_fields() {
250 let p = EpgProgramme::default();
251 assert!(p.keyword.is_empty());
252 assert!(p.orig_language.is_none());
253 assert!(p.video.is_none());
254 assert!(p.audio.is_empty());
255 assert!(p.review.is_empty());
256 }
257
258 #[test]
259 fn epg_video_default_has_all_none() {
260 let v = EpgVideo::default();
261 assert!(v.present.is_none());
262 assert!(v.colour.is_none());
263 assert!(v.aspect.is_none());
264 assert!(v.quality.is_none());
265 }
266
267 #[test]
268 fn string_with_lang_constructors() {
269 let plain = EpgStringWithLang::new("Hello");
270 assert_eq!(plain.value, "Hello");
271 assert!(plain.lang.is_none());
272
273 let lang = EpgStringWithLang::with_lang("Bonjour", "fr");
274 assert_eq!(lang.value, "Bonjour");
275 assert_eq!(lang.lang.as_deref(), Some("fr"));
276 }
277}