exif/error.rs
1//
2// Copyright (c) 2016 KAMADA Ken'ichi.
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions
7// are met:
8// 1. Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// 2. Redistributions in binary form must reproduce the above copyright
11// notice, this list of conditions and the following disclaimer in the
12// documentation and/or other materials provided with the distribution.
13//
14// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24// SUCH DAMAGE.
25//
26
27use std::error;
28use std::fmt;
29use std::io;
30use std::sync::Mutex;
31
32/// An error returned when parsing of Exif data fails.
33#[derive(Debug)]
34#[non_exhaustive]
35pub enum Error {
36 /// Input data was malformed or truncated.
37 InvalidFormat(&'static str),
38 /// Input data could not be read due to an I/O error and
39 /// a `std::io::Error` value is associated with this variant.
40 Io(io::Error),
41 /// Exif attribute information was not found in an image file
42 /// such as JPEG.
43 NotFound(&'static str),
44 /// The value of the field is blank. Some fields have blank values
45 /// whose meanings are defined as "unknown". Such a blank value
46 /// should be treated the same as the absence of the field.
47 BlankValue(&'static str),
48 /// Field values or image data are too big to encode.
49 TooBig(&'static str),
50 /// The field type is not supported and cannnot be encoded.
51 NotSupported(&'static str),
52 /// The field has an unexpected value.
53 UnexpectedValue(&'static str),
54 /// Partially-parsed result and errors. This can be returned only when
55 /// `Reader::continue_on_error` is enabled.
56 PartialResult(PartialResult),
57}
58
59impl Error {
60 /// Extracts `Exif` and `Vec<Error>` from `Error::PartialResult`.
61 ///
62 /// If `self` is `Error::PartialResult`,
63 /// ignored errors are passed to `f` as `Vec<Error>` and
64 /// partially-parsed result is retuend in `Ok`.
65 /// Otherwise, `Err(self)` is returned.
66 pub fn distill_partial_result<F>(self, f: F) -> Result<crate::Exif, Self>
67 where F: FnOnce(Vec<Error>) {
68 if let Error::PartialResult(partial) = self {
69 let (exif, errors) = partial.into_inner();
70 f(errors);
71 Ok(exif)
72 } else {
73 Err(self)
74 }
75 }
76}
77
78impl From<io::Error> for Error {
79 fn from(err: io::Error) -> Error {
80 Error::Io(err)
81 }
82}
83
84impl fmt::Display for Error {
85 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
86 match *self {
87 Error::InvalidFormat(msg) => f.write_str(msg),
88 Error::Io(ref err) => err.fmt(f),
89 Error::NotFound(ctn) => write!(f, "No Exif data found in {}", ctn),
90 Error::BlankValue(msg) => f.write_str(msg),
91 Error::TooBig(msg) => f.write_str(msg),
92 Error::NotSupported(msg) => f.write_str(msg),
93 Error::UnexpectedValue(msg) => f.write_str(msg),
94 Error::PartialResult(ref pr) =>
95 write!(f, "Partial result with {} fields and {} errors",
96 pr.0.0.lock().expect("should not panic").fields().len(),
97 pr.0.1.len()),
98 }
99 }
100}
101
102impl error::Error for Error {
103 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
104 match *self {
105 Error::InvalidFormat(_) => None,
106 Error::Io(ref err) => Some(err),
107 Error::NotFound(_) => None,
108 Error::BlankValue(_) => None,
109 Error::TooBig(_) => None,
110 Error::NotSupported(_) => None,
111 Error::UnexpectedValue(_) => None,
112 Error::PartialResult(_) => None,
113 }
114 }
115}
116
117/// Partially-parsed result and errors.
118pub struct PartialResult(Box<(Mutex<crate::Exif>, Vec<Error>)>);
119
120impl PartialResult {
121 pub(crate) fn new(exif: crate::Exif, errors: Vec<Error>) -> Self {
122 Self(Box::new((Mutex::new(exif), errors)))
123 }
124
125 /// Returns partially-parsed `Exif` and ignored `Error`s.
126 pub fn into_inner(self) -> (crate::Exif, Vec<Error>) {
127 let (exif, errors) = *self.0;
128 (exif.into_inner().expect("should not panic"), errors)
129 }
130}
131
132impl fmt::Debug for PartialResult {
133 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
134 write!(f, "PartialResult(Exif({} fields), {:?})",
135 self.0.0.lock().expect("should not panic").fields().len(),
136 self.0.1)
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143
144 // Check compatibility with anyhow::Error, which requires Send, Sync,
145 // and 'static on error types.
146 #[test]
147 fn is_send_sync_static() {
148 let _: Box<dyn Send + Sync + 'static> =
149 Box::new(Error::InvalidFormat("test"));
150 }
151}