raves_metadata 0.0.4

A library to parse metadata from media files
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
//! Error types for the [`xmp`](`crate::xmp`) module.

use std::num::{ParseFloatError, ParseIntError};

use raves_metadata_types::xmp::{
    XmpElement, XmpValue,
    parse_types::{XmpKind, XmpKindStructField},
};

/// A result obtained when parsing a single XMP value.
///
/// This may or may not contain an error.
pub type XmpValueResult = Result<XmpValue, XmpParsingError>;

/// A result obtained when parsing a single XMP element.
///
/// This may or may not contain an error.
pub type XmpElementResult = Result<XmpElement, XmpParsingError>;

use std::sync::Arc;

/// This is an error that happened while we were parsing XMP.
#[derive(Clone, Debug)]
#[repr(u8)]
pub enum XmpError {
    /// The given data was not UTF-8.
    ///
    /// Data in XMP is required to be represented in UTF-8.
    NotUtf8,

    /// `xmltree` failed to parse the XML.
    XmlParseError(
        // note: `Arc` allows us to impl `Clone`
        Arc<xmltree::ParseError>,
    ),

    /// Initial XML scanning failed - no `rdf:Rdf` element was found.
    NoRdfElement,

    /// We couldn't find any `rdf:Description` elements in the `rdf:Rdf`
    /// element.
    NoDescriptionElements,
    //
    //
    //
    //
    //
    //
    // WARNING: do not add more error variants w/o changing the `PartialEq`
    // + `PartialOrd` + `Hash` impls below.
}

impl core::cmp::PartialEq for XmpError {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            // just cmp pointers for this one lmao
            (Self::XmlParseError(a), Self::XmlParseError(b)) => {
                core::ptr::eq(Arc::as_ptr(a), Arc::as_ptr(b))
            }
            // otherwise, compare enum discriminants
            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
        }
    }
}

impl core::cmp::PartialOrd for XmpError {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        // this is so dumb lol
        //
        // why can't you compare `core::mem::Discriminant<T>`?
        fn map_to_u8(e: &XmpError) -> u8 {
            match e {
                XmpError::NotUtf8 => 0_u8,
                XmpError::XmlParseError(_) => 1_u8,
                XmpError::NoRdfElement => 2_u8,
                XmpError::NoDescriptionElements => 3_u8,
            }
        }

        match (self, other) {
            // just cmp pointers
            (Self::XmlParseError(a), Self::XmlParseError(b)) => {
                Arc::as_ptr(a).partial_cmp(&Arc::as_ptr(b))
            }

            // otherwise, compare enum discriminants
            _ => map_to_u8(self).partial_cmp(&map_to_u8(other)),
        }
    }
}

impl core::hash::Hash for XmpError {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        if let XmpError::XmlParseError(a) = self {
            Arc::as_ptr(a).hash(state);
        }
        core::mem::discriminant(self).hash(state);
    }
}

impl core::fmt::Display for XmpError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            XmpError::NotUtf8 => f.write_str("The provided XMP data was invalid. It wasn't UTF-8."),

            XmpError::XmlParseError(e) => {
                write!(f, "Encountered error while parsing XML. err: {e}")
            }

            XmpError::NoRdfElement => {
                f.write_str("The XML is missing the `rdf:Rdf` element, which is required.")
            }

            XmpError::NoDescriptionElements => f.write_str(
                "The `rdf:Rdf` element has no `rdf:Description` elements. \
                    One or more are required.",
            ),
        }
    }
}

impl core::error::Error for XmpError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            XmpError::XmlParseError(e) => Some(e.as_ref()),
            XmpError::NoRdfElement | XmpError::NoDescriptionElements | XmpError::NotUtf8 => None,
        }
    }
}

impl From<xmltree::ParseError> for XmpError {
    fn from(value: xmltree::ParseError) -> Self {
        XmpError::XmlParseError(value.into())
    }
}

/// This error occurred in internal parsing.
///
/// We use it for better diagnostics. Note that these are usually converted
/// into `None` with `.inspect_err(log::error!(/* ... */)).ok()`, which
/// provides logs, but doesn't give the user direct error values to sift
/// through.
#[derive(Debug)]
pub enum XmpParsingError {
    //
    //
    //
    //
    //
    // `XmpElement` creation
    //
    /// Couldn't create an `XmpElement` from the `self: &Element` and
    /// `value: Value` pair, as `self` lacks a namespace.
    XmpElementCreationNoNamespace {
        /// The element in question.
        element_name: String,
    },

    /// Same as above, except `self` lacks a prefix.
    XmpElementCreationNoPrefix {
        /// The element in question.
        element_name: String,
    },

    //
    //
    //
    //
    //
    // related to primitive parsing
    //
    /// A primitive was given, and we were told to parse out a Boolean.
    ///
    /// However, it wasn't a matching value! The contained value was what we
    /// got.
    PrimitiveUnknownBool(
        /// The string value encountered instead of a boolean value.
        String,
    ),

    /// We were told to parse out an Integer, but it failed to parse
    /// correctly. Contained value is what we got and the `core` parsing error.
    PrimitiveIntegerParseFail(
        /// The integer source string that couldn't be parsed.
        String,
        /// The parsing error obtained from `core`.
        ParseIntError,
    ),

    /// We were told to parse out a float (Real), but didn't parse right.
    PrimitiveRealParseFail(
        /// The float source string that couldn't be parsed.
        String,
        /// The parsing error obtained from `core`.
        ParseFloatError,
    ),

    /// A primitive with a known text value had no text.
    PrimitiveTextHadNoText {
        /// The name of the element in question.
        element_name: String,
    },

    //
    //
    //
    //
    //
    // union parsing
    //
    /// Unions are currently expected to have only a `Text` discriminant, but
    /// this value was described by another `Kind`.
    UnionDiscriminantWasntText {
        /// The element's name.
        element_name: String,
        /// The kind of discriminant that wasn't a `Text` value.
        discriminant_kind: &'static XmpKindStructField,
    },

    /// The union had no discriminant, so we couldn't see how to parse it.
    UnionNoDiscriminant {
        /// The element's name.
        element_name: String,
    },

    //
    //
    //
    //
    //
    // array parsing
    //
    /// Couldn't find an inner collection type, like `rdf:Alt`, `rdf:Bag` or
    /// `rdf:Seq`.
    ArrayNoInnerCollectionType {
        /// The element's name.
        element_name: String,
        /// A list of unparsed children.
        children: Vec<xmltree::XMLNode>,
    },

    /// "Alternatives" arrays must have a default value.
    ///
    /// This one didn't.
    ArrayAltNoDefault {
        /// The element's name.
        element_name: String,

        /// The list of alternatives.
        ///
        /// One of these should have a default value, but none did!
        alternatives_array: Vec<(String, XmpElement)>,
    },

    /// The list (un/ordered array) parser was given a schema for, e.g., a
    /// struct.
    ///
    /// We can't continue parsing that since we need to know our internal type.
    ArrayGivenNonArraySchema {
        /// Element's name.
        element_name: String,

        /// Unexpected scheme that was found.
        weird_schema: &'static XmpKind,
    },

    //
    //
    //
    //
    //
    //
    // generic (schema-less) parsing error variants
    //
    /// We couldn't get the text for an element that was expected to be a
    /// primitive.
    GenericLikelyPrimitiveHadNoText {
        /// Element's name.
        element_name: String,
    },

    /// We looked through all the possible types this value could have, but it
    /// simply had no information inside it.
    ///
    /// Thus, we returned a blank type. (which isn't useful at all)
    GenericNoOtherOption {
        /// Eleement's name.
        element_name: String,
    },
}

impl core::fmt::Display for XmpParsingError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            //
            //
            //
            //
            // element creation
            //
            XmpParsingError::XmpElementCreationNoNamespace { element_name } => write!(
                f,
                "The XML element `{element_name}` has no namespace. \
                    Couldn't create an `XmpElement`.",
            ),
            XmpParsingError::XmpElementCreationNoPrefix { element_name } => write!(
                f,
                "The XML element `{element_name}` has a namespace, but no prefix. \
                    Couldn't create an `XmpElement`.",
            ),
            //
            //
            //
            //
            //
            //
            //
            // prim parsing
            //
            XmpParsingError::PrimitiveUnknownBool(got) => write!(
                f,
                "Asked to parse out a Boolean, but the stored value wasn't \
                        an expected answer. \
                Instead, it was: `{got}`",
            ),
            XmpParsingError::PrimitiveIntegerParseFail(got, parse_int_err) => write!(
                f,
                "Asked to parse out an Integer, but the stored value wasn't right. \
                    - got: `{got}`, \
                    - err: {parse_int_err}",
            ),
            XmpParsingError::PrimitiveRealParseFail(got, parse_float_err) => write!(
                f,
                "Asked to parse out a Real, but the stored value wasn't right. \
                    - got: `{got}`, \
                    - err: {parse_float_err}",
            ),
            XmpParsingError::PrimitiveTextHadNoText { element_name } => write!(
                f,
                "Element `{element_name}` was a `Primitive::Text` kind, but didn't provide text.",
            ),
            //
            //
            //
            //
            //
            //
            //
            // unions
            //
            XmpParsingError::UnionDiscriminantWasntText {
                element_name,
                discriminant_kind,
            } => write!(
                f,
                "Union `{element_name}` had a discriminant, but it wasn't `Kind::Simple(Prim::Text)`! \
                found kind: {discriminant_kind:#?}",
            ),
            XmpParsingError::UnionNoDiscriminant { element_name } => write!(
                f,
                "Element `{element_name}` was a `Primitive::Text` kind, but didn't provide text.",
            ),
            //
            //
            //
            //
            //
            //
            //
            // arrays
            //
            XmpParsingError::ArrayNoInnerCollectionType {
                element_name,
                children,
            } => write!(
                f,
                "Array `{element_name}` had no inner collection type! \
                    - known child elements: {children:#?}",
            ),
            XmpParsingError::ArrayAltNoDefault {
                element_name,
                alternatives_array,
            } => write!(
                f,
                "Alternatives array `{element_name}` had alternatives, but didn't \
                specify a default! \
                    - found alternatives: {alternatives_array:#?}",
            ),
            XmpParsingError::ArrayGivenNonArraySchema {
                element_name,
                weird_schema,
            } => write!(
                f,
                "List-like array `{element_name}` had a weird schema - it \
                wasn't for an array. \n\
- schema: {weird_schema:?}",
            ),
            //
            //
            //
            //
            //
            //
            //
            // generic heuristics
            //
            XmpParsingError::GenericLikelyPrimitiveHadNoText { element_name } => write!(
                f,
                "Generic element `{element_name}` was a primitive, but didn't provide text.",
            ),
            XmpParsingError::GenericNoOtherOption { element_name } => {
                write!(f, "Generic element `{element_name}` was blank.",)
            }
        }
    }
}

impl core::error::Error for XmpParsingError {}