Skip to main content

quick_m3u8/
config.rs

1//! Configuration for reading HLS lines
2//!
3//! This module provides configuration options for [`crate::Reader`] along with helper API (such as
4//! [`ParsingOptionsBuilder`]) for constructing config options.
5
6use crate::tag::hls::TagName;
7use std::collections::HashSet;
8
9const ALL_KNOWN_HLS_TAG_NAMES: [TagName; 32] = [
10    TagName::M3u,
11    TagName::Version,
12    TagName::IndependentSegments,
13    TagName::Start,
14    TagName::Define,
15    TagName::Targetduration,
16    TagName::MediaSequence,
17    TagName::DiscontinuitySequence,
18    TagName::Endlist,
19    TagName::PlaylistType,
20    TagName::IFramesOnly,
21    TagName::PartInf,
22    TagName::ServerControl,
23    TagName::Inf,
24    TagName::Byterange,
25    TagName::Discontinuity,
26    TagName::Key,
27    TagName::Map,
28    TagName::ProgramDateTime,
29    TagName::Gap,
30    TagName::Bitrate,
31    TagName::Part,
32    TagName::Daterange,
33    TagName::Skip,
34    TagName::PreloadHint,
35    TagName::RenditionReport,
36    TagName::Media,
37    TagName::StreamInf,
38    TagName::IFrameStreamInf,
39    TagName::SessionData,
40    TagName::SessionKey,
41    TagName::ContentSteering,
42];
43
44/// Parsing options for the [`crate::Reader`] to follow.
45///
46/// For now the only option that can be set is `hls_tag_names_to_parse`. For convenience, a builder
47/// struct [ParsingOptionsBuilder] has been provided, to make constructing this struct easier.
48#[derive(Debug, PartialEq, Clone)]
49pub struct ParsingOptions {
50    hls_tag_names_to_parse: HashSet<TagName>,
51}
52
53impl Default for ParsingOptions {
54    fn default() -> Self {
55        Self {
56            hls_tag_names_to_parse: HashSet::from(ALL_KNOWN_HLS_TAG_NAMES),
57        }
58    }
59}
60
61impl ParsingOptions {
62    /// Start a builder for constructing the `ParsingOptions`.
63    pub fn builder() -> ParsingOptionsBuilder {
64        ParsingOptionsBuilder::new()
65    }
66
67    /// The tag names that will be parsed by the [`crate::Reader`].
68    ///
69    /// HLS tags that are not included in this list will be parsed as
70    /// [`crate::line::HlsLine::UnknownTag`].
71    pub fn hls_tag_names_to_parse(&self) -> &HashSet<TagName> {
72        &self.hls_tag_names_to_parse
73    }
74
75    pub(crate) fn is_known_name(&self, name: &'_ str) -> bool {
76        let Ok(tag_name) = TagName::try_from(name) else {
77            return false;
78        };
79        self.hls_tag_names_to_parse.contains(&tag_name)
80    }
81}
82
83/// A builder type to provide convenience for constructing [`ParsingOptions`].
84///
85/// Follows the "non-consuming" pattern defined in "[The builder pattern]".
86///
87/// [The builder pattern]: https://doc.rust-lang.org/1.12.0/style/ownership/builders.html
88#[derive(Default, Debug)]
89pub struct ParsingOptionsBuilder {
90    hls_tag_names_to_parse: HashSet<TagName>,
91}
92
93impl ParsingOptionsBuilder {
94    /// Instantiate the builder.
95    pub fn new() -> Self {
96        Self {
97            hls_tag_names_to_parse: HashSet::default(),
98        }
99    }
100
101    /// Finish building, consume the builder, and generate the [`ParsingOptions`].
102    pub fn build(&self) -> ParsingOptions {
103        ParsingOptions {
104            hls_tag_names_to_parse: self.hls_tag_names_to_parse.clone(),
105        }
106    }
107
108    /// Include parsing of all known HLS tags.
109    pub fn with_parsing_for_all_tags(&mut self) -> &mut Self {
110        self.hls_tag_names_to_parse.extend(ALL_KNOWN_HLS_TAG_NAMES);
111        self
112    }
113
114    /// Include parsing of [`crate::tag::hls::M3u`].
115    pub fn with_parsing_for_m3u(&mut self) -> &mut Self {
116        self.hls_tag_names_to_parse.insert(TagName::M3u);
117        self
118    }
119
120    /// Ignore parsing of [`crate::tag::hls::M3u`].
121    pub fn without_parsing_for_m3u(&mut self) -> &mut Self {
122        self.hls_tag_names_to_parse.remove(&TagName::M3u);
123        self
124    }
125
126    /// Include parsing of [`crate::tag::hls::Version`].
127    pub fn with_parsing_for_version(&mut self) -> &mut Self {
128        self.hls_tag_names_to_parse.insert(TagName::Version);
129        self
130    }
131
132    /// Ignore parsing of [`crate::tag::hls::Version`].
133    pub fn without_parsing_for_version(&mut self) -> &mut Self {
134        self.hls_tag_names_to_parse.remove(&TagName::Version);
135        self
136    }
137
138    /// Include parsing of [`crate::tag::hls::IndependentSegments`].
139    pub fn with_parsing_for_independent_segments(&mut self) -> &mut Self {
140        self.hls_tag_names_to_parse
141            .insert(TagName::IndependentSegments);
142        self
143    }
144
145    /// Ignore parsing of [`crate::tag::hls::IndependentSegments`].
146    pub fn without_parsing_for_independent_segments(&mut self) -> &mut Self {
147        self.hls_tag_names_to_parse
148            .remove(&TagName::IndependentSegments);
149        self
150    }
151
152    /// Include parsing of [`crate::tag::hls::Start`].
153    pub fn with_parsing_for_start(&mut self) -> &mut Self {
154        self.hls_tag_names_to_parse.insert(TagName::Start);
155        self
156    }
157
158    /// Ignore parsing of [`crate::tag::hls::Start`].
159    pub fn without_parsing_for_start(&mut self) -> &mut Self {
160        self.hls_tag_names_to_parse.remove(&TagName::Start);
161        self
162    }
163
164    /// Include parsing of [`crate::tag::hls::Define`].
165    pub fn with_parsing_for_define(&mut self) -> &mut Self {
166        self.hls_tag_names_to_parse.insert(TagName::Define);
167        self
168    }
169
170    /// Ignore parsing of [`crate::tag::hls::Define`].
171    pub fn without_parsing_for_define(&mut self) -> &mut Self {
172        self.hls_tag_names_to_parse.remove(&TagName::Define);
173        self
174    }
175
176    /// Include parsing of [`crate::tag::hls::Targetduration`].
177    pub fn with_parsing_for_targetduration(&mut self) -> &mut Self {
178        self.hls_tag_names_to_parse.insert(TagName::Targetduration);
179        self
180    }
181
182    /// Ignore parsing of [`crate::tag::hls::Targetduration`].
183    pub fn without_parsing_for_targetduration(&mut self) -> &mut Self {
184        self.hls_tag_names_to_parse.remove(&TagName::Targetduration);
185        self
186    }
187
188    /// Include parsing of [`crate::tag::hls::MediaSequence`].
189    pub fn with_parsing_for_media_sequence(&mut self) -> &mut Self {
190        self.hls_tag_names_to_parse.insert(TagName::MediaSequence);
191        self
192    }
193
194    /// Ignore parsing of [`crate::tag::hls::MediaSequence`].
195    pub fn without_parsing_for_media_sequence(&mut self) -> &mut Self {
196        self.hls_tag_names_to_parse.remove(&TagName::MediaSequence);
197        self
198    }
199
200    /// Include parsing of [`crate::tag::hls::DiscontinuitySequence`].
201    pub fn with_parsing_for_discontinuity_sequence(&mut self) -> &mut Self {
202        self.hls_tag_names_to_parse
203            .insert(TagName::DiscontinuitySequence);
204        self
205    }
206
207    /// Ignore parsing of [`crate::tag::hls::DiscontinuitySequence`].
208    pub fn without_parsing_for_discontinuity_sequence(&mut self) -> &mut Self {
209        self.hls_tag_names_to_parse
210            .remove(&TagName::DiscontinuitySequence);
211        self
212    }
213
214    /// Include parsing of [`crate::tag::hls::Endlist`].
215    pub fn with_parsing_for_endlist(&mut self) -> &mut Self {
216        self.hls_tag_names_to_parse.insert(TagName::Endlist);
217        self
218    }
219
220    /// Ignore parsing of [`crate::tag::hls::Endlist`].
221    pub fn without_parsing_for_endlist(&mut self) -> &mut Self {
222        self.hls_tag_names_to_parse.remove(&TagName::Endlist);
223        self
224    }
225
226    /// Include parsing of [`crate::tag::hls::PlaylistType`].
227    pub fn with_parsing_for_playlist_type(&mut self) -> &mut Self {
228        self.hls_tag_names_to_parse.insert(TagName::PlaylistType);
229        self
230    }
231
232    /// Ignore parsing of [`crate::tag::hls::PlaylistType`].
233    pub fn without_parsing_for_playlist_type(&mut self) -> &mut Self {
234        self.hls_tag_names_to_parse.remove(&TagName::PlaylistType);
235        self
236    }
237
238    /// Include parsing of [`crate::tag::hls::IFramesOnly`].
239    pub fn with_parsing_for_i_frames_only(&mut self) -> &mut Self {
240        self.hls_tag_names_to_parse.insert(TagName::IFramesOnly);
241        self
242    }
243
244    /// Ignore parsing of [`crate::tag::hls::IFramesOnly`].
245    pub fn without_parsing_for_i_frames_only(&mut self) -> &mut Self {
246        self.hls_tag_names_to_parse.remove(&TagName::IFramesOnly);
247        self
248    }
249
250    /// Include parsing of [`crate::tag::hls::PartInf`].
251    pub fn with_parsing_for_part_inf(&mut self) -> &mut Self {
252        self.hls_tag_names_to_parse.insert(TagName::PartInf);
253        self
254    }
255
256    /// Ignore parsing of [`crate::tag::hls::PartInf`].
257    pub fn without_parsing_for_part_inf(&mut self) -> &mut Self {
258        self.hls_tag_names_to_parse.remove(&TagName::PartInf);
259        self
260    }
261
262    /// Include parsing of [`crate::tag::hls::ServerControl`].
263    pub fn with_parsing_for_server_control(&mut self) -> &mut Self {
264        self.hls_tag_names_to_parse.insert(TagName::ServerControl);
265        self
266    }
267
268    /// Ignore parsing of [`crate::tag::hls::ServerControl`].
269    pub fn without_parsing_for_server_control(&mut self) -> &mut Self {
270        self.hls_tag_names_to_parse.remove(&TagName::ServerControl);
271        self
272    }
273
274    /// Include parsing of [`crate::tag::hls::Inf`].
275    pub fn with_parsing_for_inf(&mut self) -> &mut Self {
276        self.hls_tag_names_to_parse.insert(TagName::Inf);
277        self
278    }
279
280    /// Ignore parsing of [`crate::tag::hls::Inf`].
281    pub fn without_parsing_for_inf(&mut self) -> &mut Self {
282        self.hls_tag_names_to_parse.remove(&TagName::Inf);
283        self
284    }
285
286    /// Include parsing of [`crate::tag::hls::Byterange`].
287    pub fn with_parsing_for_byterange(&mut self) -> &mut Self {
288        self.hls_tag_names_to_parse.insert(TagName::Byterange);
289        self
290    }
291
292    /// Ignore parsing of [`crate::tag::hls::Byterange`].
293    pub fn without_parsing_for_byterange(&mut self) -> &mut Self {
294        self.hls_tag_names_to_parse.remove(&TagName::Byterange);
295        self
296    }
297
298    /// Include parsing of [`crate::tag::hls::Discontinuity`].
299    pub fn with_parsing_for_discontinuity(&mut self) -> &mut Self {
300        self.hls_tag_names_to_parse.insert(TagName::Discontinuity);
301        self
302    }
303
304    /// Ignore parsing of [`crate::tag::hls::Discontinuity`].
305    pub fn without_parsing_for_discontinuity(&mut self) -> &mut Self {
306        self.hls_tag_names_to_parse.remove(&TagName::Discontinuity);
307        self
308    }
309
310    /// Include parsing of [`crate::tag::hls::Key`].
311    pub fn with_parsing_for_key(&mut self) -> &mut Self {
312        self.hls_tag_names_to_parse.insert(TagName::Key);
313        self
314    }
315
316    /// Ignore parsing of [`crate::tag::hls::Key`].
317    pub fn without_parsing_for_key(&mut self) -> &mut Self {
318        self.hls_tag_names_to_parse.remove(&TagName::Key);
319        self
320    }
321
322    /// Include parsing of [`crate::tag::hls::Map`].
323    pub fn with_parsing_for_map(&mut self) -> &mut Self {
324        self.hls_tag_names_to_parse.insert(TagName::Map);
325        self
326    }
327
328    /// Ignore parsing of [`crate::tag::hls::Map`].
329    pub fn without_parsing_for_map(&mut self) -> &mut Self {
330        self.hls_tag_names_to_parse.remove(&TagName::Map);
331        self
332    }
333
334    /// Include parsing of [`crate::tag::hls::ProgramDateTime`].
335    pub fn with_parsing_for_program_date_time(&mut self) -> &mut Self {
336        self.hls_tag_names_to_parse.insert(TagName::ProgramDateTime);
337        self
338    }
339
340    /// Ignore parsing of [`crate::tag::hls::ProgramDateTime`].
341    pub fn without_parsing_for_program_date_time(&mut self) -> &mut Self {
342        self.hls_tag_names_to_parse
343            .remove(&TagName::ProgramDateTime);
344        self
345    }
346
347    /// Include parsing of [`crate::tag::hls::Gap`].
348    pub fn with_parsing_for_gap(&mut self) -> &mut Self {
349        self.hls_tag_names_to_parse.insert(TagName::Gap);
350        self
351    }
352
353    /// Ignore parsing of [`crate::tag::hls::Gap`].
354    pub fn without_parsing_for_gap(&mut self) -> &mut Self {
355        self.hls_tag_names_to_parse.remove(&TagName::Gap);
356        self
357    }
358
359    /// Include parsing of [`crate::tag::hls::Bitrate`].
360    pub fn with_parsing_for_bitrate(&mut self) -> &mut Self {
361        self.hls_tag_names_to_parse.insert(TagName::Bitrate);
362        self
363    }
364
365    /// Ignore parsing of [`crate::tag::hls::Bitrate`].
366    pub fn without_parsing_for_bitrate(&mut self) -> &mut Self {
367        self.hls_tag_names_to_parse.remove(&TagName::Bitrate);
368        self
369    }
370
371    /// Include parsing of [`crate::tag::hls::Part`].
372    pub fn with_parsing_for_part(&mut self) -> &mut Self {
373        self.hls_tag_names_to_parse.insert(TagName::Part);
374        self
375    }
376
377    /// Ignore parsing of [`crate::tag::hls::Part`].
378    pub fn without_parsing_for_part(&mut self) -> &mut Self {
379        self.hls_tag_names_to_parse.remove(&TagName::Part);
380        self
381    }
382
383    /// Include parsing of [`crate::tag::hls::Daterange`].
384    pub fn with_parsing_for_daterange(&mut self) -> &mut Self {
385        self.hls_tag_names_to_parse.insert(TagName::Daterange);
386        self
387    }
388
389    /// Ignore parsing of [`crate::tag::hls::Daterange`].
390    pub fn without_parsing_for_daterange(&mut self) -> &mut Self {
391        self.hls_tag_names_to_parse.remove(&TagName::Daterange);
392        self
393    }
394
395    /// Include parsing of [`crate::tag::hls::Skip`].
396    pub fn with_parsing_for_skip(&mut self) -> &mut Self {
397        self.hls_tag_names_to_parse.insert(TagName::Skip);
398        self
399    }
400
401    /// Ignore parsing of [`crate::tag::hls::Skip`].
402    pub fn without_parsing_for_skip(&mut self) -> &mut Self {
403        self.hls_tag_names_to_parse.remove(&TagName::Skip);
404        self
405    }
406
407    /// Include parsing of [`crate::tag::hls::PreloadHint`].
408    pub fn with_parsing_for_preload_hint(&mut self) -> &mut Self {
409        self.hls_tag_names_to_parse.insert(TagName::PreloadHint);
410        self
411    }
412
413    /// Ignore parsing of [`crate::tag::hls::PreloadHint`].
414    pub fn without_parsing_for_preload_hint(&mut self) -> &mut Self {
415        self.hls_tag_names_to_parse.remove(&TagName::PreloadHint);
416        self
417    }
418
419    /// Include parsing of [`crate::tag::hls::RenditionReport`].
420    pub fn with_parsing_for_rendition_report(&mut self) -> &mut Self {
421        self.hls_tag_names_to_parse.insert(TagName::RenditionReport);
422        self
423    }
424
425    /// Ignore parsing of [`crate::tag::hls::RenditionReport`].
426    pub fn without_parsing_for_rendition_report(&mut self) -> &mut Self {
427        self.hls_tag_names_to_parse
428            .remove(&TagName::RenditionReport);
429        self
430    }
431
432    /// Include parsing of [`crate::tag::hls::Media`].
433    pub fn with_parsing_for_media(&mut self) -> &mut Self {
434        self.hls_tag_names_to_parse.insert(TagName::Media);
435        self
436    }
437
438    /// Ignore parsing of [`crate::tag::hls::Media`].
439    pub fn without_parsing_for_media(&mut self) -> &mut Self {
440        self.hls_tag_names_to_parse.remove(&TagName::Media);
441        self
442    }
443
444    /// Include parsing of [`crate::tag::hls::StreamInf`].
445    pub fn with_parsing_for_stream_inf(&mut self) -> &mut Self {
446        self.hls_tag_names_to_parse.insert(TagName::StreamInf);
447        self
448    }
449
450    /// Ignore parsing of [`crate::tag::hls::StreamInf`].
451    pub fn without_parsing_for_stream_inf(&mut self) -> &mut Self {
452        self.hls_tag_names_to_parse.remove(&TagName::StreamInf);
453        self
454    }
455
456    /// Include parsing of [`crate::tag::hls::IFrameStreamInf`].
457    pub fn with_parsing_for_i_frame_stream_inf(&mut self) -> &mut Self {
458        self.hls_tag_names_to_parse.insert(TagName::IFrameStreamInf);
459        self
460    }
461
462    /// Ignore parsing of [`crate::tag::hls::IFrameStreamInf`].
463    pub fn without_parsing_for_i_frame_stream_inf(&mut self) -> &mut Self {
464        self.hls_tag_names_to_parse
465            .remove(&TagName::IFrameStreamInf);
466        self
467    }
468
469    /// Include parsing of [`crate::tag::hls::SessionData`].
470    pub fn with_parsing_for_session_data(&mut self) -> &mut Self {
471        self.hls_tag_names_to_parse.insert(TagName::SessionData);
472        self
473    }
474
475    /// Ignore parsing of [`crate::tag::hls::SessionData`].
476    pub fn without_parsing_for_session_data(&mut self) -> &mut Self {
477        self.hls_tag_names_to_parse.remove(&TagName::SessionData);
478        self
479    }
480
481    /// Include parsing of [`crate::tag::hls::SessionKey`].
482    pub fn with_parsing_for_session_key(&mut self) -> &mut Self {
483        self.hls_tag_names_to_parse.insert(TagName::SessionKey);
484        self
485    }
486
487    /// Ignore parsing of [`crate::tag::hls::SessionKey`].
488    pub fn without_parsing_for_session_key(&mut self) -> &mut Self {
489        self.hls_tag_names_to_parse.remove(&TagName::SessionKey);
490        self
491    }
492
493    /// Include parsing of [`crate::tag::hls::ContentSteering`].
494    pub fn with_parsing_for_content_steering(&mut self) -> &mut Self {
495        self.hls_tag_names_to_parse.insert(TagName::ContentSteering);
496        self
497    }
498
499    /// Ignore parsing of [`crate::tag::hls::ContentSteering`].
500    pub fn without_parsing_for_content_steering(&mut self) -> &mut Self {
501        self.hls_tag_names_to_parse
502            .remove(&TagName::ContentSteering);
503        self
504    }
505}
506
507#[cfg(test)]
508mod tests {
509    use super::*;
510    use pretty_assertions::assert_eq;
511
512    #[test]
513    fn builder_with_all_tag_names() {
514        let options = ParsingOptionsBuilder::new()
515            .with_parsing_for_all_tags()
516            .build();
517        let mut count = 0;
518        for name in options.hls_tag_names_to_parse {
519            count += 1;
520            assert!(ALL_KNOWN_HLS_TAG_NAMES.contains(&name));
521        }
522        assert_eq!(32, count);
523    }
524
525    #[test]
526    fn builder_with_some_tag_names() {
527        let options = ParsingOptionsBuilder::new()
528            .with_parsing_for_bitrate()
529            .with_parsing_for_byterange()
530            .with_parsing_for_daterange()
531            .build();
532        assert!(options.hls_tag_names_to_parse.contains(&TagName::Bitrate));
533        assert!(options.hls_tag_names_to_parse.contains(&TagName::Byterange));
534        assert!(options.hls_tag_names_to_parse.contains(&TagName::Daterange));
535        assert_eq!(3, options.hls_tag_names_to_parse.len());
536    }
537
538    #[test]
539    fn builder_with_removing_some_tag_names() {
540        let options = ParsingOptionsBuilder::new()
541            .with_parsing_for_all_tags()
542            .without_parsing_for_define()
543            .without_parsing_for_i_frame_stream_inf()
544            .build();
545        assert!(!options.hls_tag_names_to_parse.contains(&TagName::Define));
546        assert!(
547            !options
548                .hls_tag_names_to_parse
549                .contains(&TagName::IFrameStreamInf)
550        );
551        assert_eq!(30, options.hls_tag_names_to_parse.len());
552    }
553}