Skip to main content

dash_runtime/
error.rs

1//! Typed errors for MPD parsing.
2
3use broadcast_common::impl_spec_display;
4use thiserror::Error;
5
6/// Which bound of [`crate::limits::Limits`] was exceeded.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8#[non_exhaustive]
9pub enum Limit {
10    /// Document size in bytes.
11    InputBytes,
12    /// Element nesting depth.
13    Depth,
14    /// Total element count.
15    Elements,
16    /// Length of one attribute value.
17    AttributeBytes,
18    /// Attributes on one element.
19    Attributes,
20    /// `Period` count.
21    Periods,
22    /// `AdaptationSet` count in one Period.
23    AdaptationSets,
24    /// `Representation` count in one AdaptationSet.
25    Representations,
26    /// `S` entries in one `SegmentTimeline`.
27    TimelineEntries,
28    /// `BaseURL` alternates on one level.
29    BaseUrls,
30    /// `SegmentURL` entries in one `SegmentList`.
31    SegmentUrls,
32}
33
34impl Limit {
35    /// Stable snake_case label.
36    #[must_use]
37    pub fn name(&self) -> &'static str {
38        match self {
39            Self::InputBytes => "input_bytes",
40            Self::Depth => "depth",
41            Self::Elements => "elements",
42            Self::AttributeBytes => "attribute_bytes",
43            Self::Attributes => "attributes",
44            Self::Periods => "periods",
45            Self::AdaptationSets => "adaptation_sets",
46            Self::Representations => "representations",
47            Self::TimelineEntries => "timeline_entries",
48            Self::BaseUrls => "base_urls",
49            Self::SegmentUrls => "segment_urls",
50        }
51    }
52}
53impl_spec_display!(Limit);
54
55impl From<broadcast_common::xml_chars::XmlLimit> for Limit {
56    fn from(limit: broadcast_common::xml_chars::XmlLimit) -> Self {
57        use broadcast_common::xml_chars::XmlLimit;
58        match limit {
59            XmlLimit::Depth => Self::Depth,
60            XmlLimit::Elements => Self::Elements,
61            XmlLimit::AttributeBytes => Self::AttributeBytes,
62            XmlLimit::Attributes => Self::Attributes,
63            _ => Self::InputBytes,
64        }
65    }
66}
67
68/// A feature the 0.1 core recognises but deliberately does not implement.
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70#[non_exhaustive]
71pub enum Unsupported {
72    /// `MPD@type="list"` (ISO/IEC 23009-1 Ed.6).
73    PresentationTypeList,
74    /// Segment sequences: `S@k` above 1 or a `$SubNumber$` template.
75    SegmentSequence,
76    /// A `data:` URL used as a Segment address (5.3.9.1 b).
77    DataUrl,
78    /// A byte range with no last byte (`a-`).
79    OpenEndedRange,
80    /// A `sidx` reference that points at another `sidx`.
81    HierarchicalSidx,
82}
83
84impl Unsupported {
85    /// Stable snake_case label.
86    #[must_use]
87    pub fn name(&self) -> &'static str {
88        match self {
89            Self::PresentationTypeList => "presentation_type_list",
90            Self::SegmentSequence => "segment_sequence",
91            Self::DataUrl => "data_url",
92            Self::OpenEndedRange => "open_ended_range",
93            Self::HierarchicalSidx => "hierarchical_sidx",
94        }
95    }
96}
97impl_spec_display!(Unsupported);
98
99/// Why an MPD document was rejected.
100#[derive(Debug, Clone, PartialEq, Eq, Error)]
101#[non_exhaustive]
102pub enum MpdError {
103    /// The bytes are not UTF-8.
104    #[error("MPD is not valid UTF-8 (valid up to byte {valid_up_to})")]
105    InvalidUtf8 {
106        /// Length of the valid prefix.
107        valid_up_to: usize,
108    },
109    /// The document is not well-formed XML (or uses a construct this parser
110    /// refuses, such as a DOCTYPE).
111    #[error("XML error at byte {pos}: {message}")]
112    Xml {
113        /// Byte offset where the reader stopped.
114        pos: usize,
115        /// What was wrong.
116        message: String,
117    },
118    /// The root element is not `MPD` in the MPD namespace.
119    #[error("document root is not an MPD in the MPD namespace (found `{found}`)")]
120    NotMpd {
121        /// Local name of the root element.
122        found: String,
123    },
124    /// An attribute or element violates the schema rules the core relies on.
125    #[error("invalid MPD: {what}: {detail}")]
126    Invalid {
127        /// The attribute or rule.
128        what: &'static str,
129        /// The offending value or an explanation.
130        detail: String,
131    },
132    /// A hostile-input bound was exceeded.
133    #[error("MPD exceeds the {limit} limit of {max}")]
134    TooLarge {
135        /// Which bound.
136        limit: Limit,
137        /// Its value.
138        max: usize,
139    },
140    /// A feature outside the 0.1 scope.
141    #[error("unsupported: {0}")]
142    Unsupported(Unsupported),
143}