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

//! Error type definitions.

use std::borrow::Cow;
use std::io::ErrorKind;
pub use std::io::Error as IoError;
pub use std::io::Result as IoResult;
use std::convert::TryFrom;
use std::error;
use std::fmt;
use std::num::TryFromIntError;


// Export types

/// A result that may contain an exr error.
pub type Result<T> = std::result::Result<T, Error>;

/// A result that, if ok, contains nothing, and otherwise contains an exr error.
pub type UnitResult = Result<()>;


/// An error that may happen while reading or writing an exr file.
/// Distinguishes between three types of errors:
/// unsupported features, invalid data, and file system errors.
#[derive(Debug)]
pub enum Error {

    /// Reading or Writing the file has been aborted by the caller.
    /// This error will never be triggered by this crate itself,
    /// only by users of this library.
    /// It exists to be returned from a progress callback.
    Aborted, // FIXME remove?? is not used really?

    /// The contents of the file are not supported by
    /// this specific implementation of open exr,
    /// even though the data may be valid.
    NotSupported(Cow<'static, str>),

    /// The contents of the image are contradicting or insufficient.
    /// Also returned for `ErrorKind::UnexpectedEof` errors.
    Invalid(Cow<'static, str>),

    /// The underlying byte stream could not be read successfully,
    /// probably due to file system related errors.
    Io(IoError),
}


impl Error {

    /// Create an error of the variant `Invalid`.
    pub(crate) fn invalid(message: impl Into<Cow<'static, str>>) -> Self {
        Error::Invalid(message.into())
    }

    /// Create an error of the variant `NotSupported`.
    pub(crate) fn unsupported(message: impl Into<Cow<'static, str>>) -> Self {
        Error::NotSupported(message.into())
    }
}

/// Enable using the `?` operator on `std::io::Result`.
impl From<IoError> for Error {
    fn from(error: IoError) -> Self {
        if error.kind() == ErrorKind::UnexpectedEof {
            Error::invalid("reference to missing bytes")
        }
        else {
            Error::Io(error)
        }
    }
}

// TODO use `usize::try_from(x)?` everywhere
impl From<TryFromIntError> for Error {
    fn from(_: TryFromIntError) -> Self {
        Error::invalid("invalid size")
    }
}

impl error::Error for Error {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match *self {
            Error::Io(ref err) => Some(err),
            _ => None,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Error::Io(err) => err.fmt(formatter),
            Error::NotSupported(message) => write!(formatter, "not supported: {}", message),
            Error::Invalid(message) => write!(formatter, "invalid: {}", message),
            Error::Aborted => write!(formatter, "cancelled"),
        }
    }
}

/// Return error on invalid range.
#[inline]
pub(crate) fn i32_to_usize(value: i32, error_message: &'static str) -> Result<usize> {
    usize::try_from(value).map_err(|_| Error::invalid(error_message))
}

/// Return error on invalid range.
#[inline]
pub(crate) fn usize_to_u16(value: usize) -> Result<u16> {
    Ok(u16::try_from(value)?)
}

/// Panic on overflow.
#[inline]
pub(crate) fn u64_to_usize(value: u64) -> usize {
    usize::try_from(value).expect("(u64 as usize) overflowed")
}

/// Panic on overflow.
#[inline]
pub(crate) fn u32_to_usize(value: u32) -> usize {
    usize::try_from(value).expect("(u32 as usize) overflowed")
}

/// Panic on overflow.
#[inline]
pub(crate) fn usize_to_i32(value: usize) -> i32 {
    i32::try_from(value).expect("(usize as i32) overflowed")
}

/// Panic on overflow.
#[inline]
pub(crate) fn usize_to_u64(value: usize) -> u64 {
    u64::try_from(value).expect("(usize as u64) overflowed")
}