nom_exif/
error.rs

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
use std::{
    fmt::{Debug, Display},
    io::{self},
    string::FromUtf8Error,
};
use thiserror::Error;

type FallbackError = Box<dyn std::error::Error + Send + Sync>;

#[derive(Debug, Error)]
pub enum Error {
    #[error("parse failed: {0}")]
    ParseFailed(FallbackError),

    #[error("io error: {0}")]
    IOError(std::io::Error),

    /// If you encounter this error, please consider filing a bug on github
    #[error("unrecognized file format")]
    UnrecognizedFileFormat,
}

#[derive(Debug, Error)]
pub(crate) enum ParsedError {
    #[error("no enough bytes")]
    NoEnoughBytes,

    #[error("io error: {0}")]
    IOError(std::io::Error),

    #[error("{0}")]
    Failed(String),
}

/// Due to the fact that metadata in MOV files is typically located at the end
/// of the file, conventional parsing methods would require reading a
/// significant amount of unnecessary data during the parsing process. This
/// would impact the performance of the parsing program and consume more memory.
///
/// To address this issue, we have defined an `Error::Skip` enumeration type to
/// inform the caller that certain bytes in the parsing process are not required
/// and can be skipped directly. The specific method of skipping can be
/// determined by the caller based on the situation. For example:
///
/// - For files, you can quickly skip using a `Seek` operation.
///
/// - For network byte streams, you may need to skip these bytes through read
///   operations, or preferably, by designing an appropriate network protocol for
///   skipping.
///
/// # [`ParsingError::Skip`]
///
/// Please note that when the caller receives an `Error::Skip(n)` error, it
/// should be understood as follows:
///
/// - The parsing program has already consumed all available data and needs to
///   skip n bytes further.
///
/// - After skipping n bytes, it should continue to read subsequent data to fill
///   the buffer and use it as input for the parsing function.
///
/// - The next time the parsing function is called (usually within a loop), the
///   previously consumed data (including the skipped bytes) should be ignored,
///   and only the newly read data should be passed in.
///
/// # [`ParsingError::Need`]
///
/// Additionally, to simplify error handling, we have integrated
/// `nom::Err::Incomplete` error into `Error::Need`. This allows us to use the
/// same error type to notify the caller that we require more bytes to continue
/// parsing.
#[derive(Debug, Error)]
pub(crate) enum ParsingError {
    #[error("need more bytes: {0}")]
    Need(usize),

    #[error("clear and skip bytes: {0:?}")]
    ClearAndSkip(usize),

    #[error("{0}")]
    Failed(String),
}

#[derive(Debug, Error)]
pub(crate) struct ParsingErrorState {
    pub err: ParsingError,
    pub state: Option<ParsingState>,
}

impl ParsingErrorState {
    pub fn new(err: ParsingError, state: Option<ParsingState>) -> Self {
        Self { err, state }
    }
}

impl Display for ParsingErrorState {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        Display::fmt(
            &format!(
                "ParsingError(err: {}, state: {})",
                self.err,
                self.state
                    .as_ref()
                    .map(|x| x.to_string())
                    .unwrap_or("None".to_string())
            ),
            f,
        )
    }
}

impl From<&str> for ParsingError {
    fn from(value: &str) -> Self {
        Self::Failed(value.to_string())
    }
}

impl From<std::io::Error> for ParsedError {
    fn from(value: std::io::Error) -> Self {
        Self::IOError(value)
    }
}

impl From<ParsedError> for crate::Error {
    fn from(value: ParsedError) -> Self {
        match value {
            ParsedError::NoEnoughBytes => Self::ParseFailed(value.into()),
            ParsedError::IOError(e) => Self::IOError(e),
            ParsedError::Failed(e) => Self::ParseFailed(e.into()),
        }
    }
}

use Error::*;

use crate::parser::ParsingState;

impl From<io::Error> for Error {
    fn from(value: io::Error) -> Self {
        ParseFailed(value.into())
    }
}

impl From<String> for Error {
    fn from(src: String) -> Error {
        ParseFailed(src.into())
    }
}

impl From<&str> for Error {
    fn from(src: &str) -> Error {
        src.to_string().into()
    }
}

impl From<FromUtf8Error> for Error {
    fn from(value: FromUtf8Error) -> Self {
        ParseFailed(value.into())
    }
}

impl<T: Debug> From<nom::Err<nom::error::Error<T>>> for crate::Error {
    fn from(e: nom::Err<nom::error::Error<T>>) -> Self {
        convert_parse_error(e, "")
    }
}

pub(crate) fn convert_parse_error<T: Debug>(
    e: nom::Err<nom::error::Error<T>>,
    message: &str,
) -> Error {
    let s = match e {
        nom::Err::Incomplete(_) => format!("{e}; {message}"),
        nom::Err::Error(e) => format!("{}; {message}", e.code.description()),
        nom::Err::Failure(e) => format!("{}; {message}", e.code.description()),
    };

    s.into()
}

impl From<nom::Err<nom::error::Error<&[u8]>>> for ParsingError {
    fn from(e: nom::Err<nom::error::Error<&[u8]>>) -> Self {
        match e {
            nom::Err::Incomplete(needed) => match needed {
                nom::Needed::Unknown => ParsingError::Need(1),
                nom::Needed::Size(n) => ParsingError::Need(n.get()),
            },
            nom::Err::Failure(e) | nom::Err::Error(e) => {
                ParsingError::Failed(e.code.description().to_string())
            }
        }
    }
}

// impl From<nom::Err<nom::error::Error<&[u8]>>> for ParsingErrorState {
//     fn from(e: nom::Err<nom::error::Error<&[u8]>>) -> Self {
//         match e {
//             nom::Err::Incomplete(needed) => match needed {
//                 nom::Needed::Unknown => ParsingErrorState::new(ParsingError::Need(1), None),
//                 nom::Needed::Size(n) => ParsingErrorState::new(ParsingError::Need(n.get()), None),
//             },
//             nom::Err::Failure(e) | nom::Err::Error(e) => {
//                 ParsingErrorState::new(ParsingError::Failed(e.code.description().to_string()), None)
//             }
//         }
//     }
// }

pub(crate) fn nom_error_to_parsing_error_with_state(
    e: nom::Err<nom::error::Error<&[u8]>>,
    state: Option<ParsingState>,
) -> ParsingErrorState {
    match e {
        nom::Err::Incomplete(needed) => match needed {
            nom::Needed::Unknown => ParsingErrorState::new(ParsingError::Need(1), state),
            nom::Needed::Size(n) => ParsingErrorState::new(ParsingError::Need(n.get()), state),
        },
        nom::Err::Failure(e) | nom::Err::Error(e) => ParsingErrorState::new(
            ParsingError::Failed(e.code.description().to_string()),
            state,
        ),
    }
}