Skip to main content

broadcast_dash/
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
68impl From<broadcast_common::xml::XmlError> for MpdError {
69    fn from(e: broadcast_common::xml::XmlError) -> Self {
70        use broadcast_common::xml::XmlError;
71        match e {
72            XmlError::InvalidUtf8 { valid_up_to } => Self::InvalidUtf8 { valid_up_to },
73            XmlError::NotWellFormed { position, message } => Self::Xml {
74                pos: position,
75                message,
76            },
77            XmlError::WrongRoot { found } => Self::NotMpd { found },
78            XmlError::Limit(exceeded) => Self::TooLarge {
79                limit: exceeded.limit.into(),
80                max: exceeded.max,
81            },
82            _ => Self::Invalid {
83                what: "document",
84                detail: "unrecognised XML reader failure".to_string(),
85            },
86        }
87    }
88}
89
90/// A feature outside the read-only core's scope.
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92#[non_exhaustive]
93pub enum Unsupported {
94    /// `MPD@type="list"` (ISO/IEC 23009-1 Ed.6).
95    PresentationTypeList,
96    /// Segment sequences: `S@k` above 1 or a `$SubNumber$` template.
97    SegmentSequence,
98    /// A `data:` URL used as a Segment address (5.3.9.1 b).
99    DataUrl,
100    /// A byte range with no last byte (`a-`).
101    OpenEndedRange,
102    /// A `sidx` reference that points at another `sidx`.
103    HierarchicalSidx,
104    /// An `Event` whose content is an XML payload rather than text.
105    EventXmlPayload,
106    /// A `Preselection` child the model does not carry: `Switching`,
107    /// `RandomAccess`, `ContentPopularityRate` or `SegmentSequenceProperties`.
108    RepresentationBaseChild,
109}
110
111impl Unsupported {
112    /// Stable snake_case label.
113    #[must_use]
114    pub fn name(&self) -> &'static str {
115        match self {
116            Self::PresentationTypeList => "presentation_type_list",
117            Self::SegmentSequence => "segment_sequence",
118            Self::DataUrl => "data_url",
119            Self::OpenEndedRange => "open_ended_range",
120            Self::HierarchicalSidx => "hierarchical_sidx",
121            Self::EventXmlPayload => "event_xml_payload",
122            Self::RepresentationBaseChild => "representation_base_child",
123        }
124    }
125}
126impl_spec_display!(Unsupported);
127
128/// Why an MPD document was rejected.
129#[derive(Debug, Clone, PartialEq, Eq, Error)]
130#[non_exhaustive]
131pub enum MpdError {
132    /// The bytes are not UTF-8.
133    #[error("MPD is not valid UTF-8 (valid up to byte {valid_up_to})")]
134    InvalidUtf8 {
135        /// Length of the valid prefix.
136        valid_up_to: usize,
137    },
138    /// The document is not well-formed XML (or uses a construct this parser
139    /// refuses, such as a DOCTYPE).
140    #[error("XML error at byte {pos}: {message}")]
141    Xml {
142        /// Byte offset where the reader stopped.
143        pos: usize,
144        /// What was wrong.
145        message: String,
146    },
147    /// The root element is not `MPD` in the MPD namespace.
148    #[error("document root is not an MPD in the MPD namespace (found `{found}`)")]
149    NotMpd {
150        /// Local name of the root element.
151        found: String,
152    },
153    /// Serialising the model to XML failed.
154    #[error("MPD write failed: {message}")]
155    Write {
156        /// What went wrong.
157        message: String,
158    },
159    /// An attribute or element violates the schema rules the core relies on.
160    #[error("invalid MPD: {what}: {detail}")]
161    Invalid {
162        /// The attribute or rule.
163        what: &'static str,
164        /// The offending value or an explanation.
165        detail: String,
166    },
167    /// A hostile-input bound was exceeded.
168    #[error("MPD exceeds the {limit} limit of {max}")]
169    TooLarge {
170        /// Which bound.
171        limit: Limit,
172        /// Its value.
173        max: usize,
174    },
175    /// A feature outside the 0.1 scope.
176    #[error("unsupported: {0}")]
177    Unsupported(Unsupported),
178}