nom_exif/
error.rs

1use std::{
2    fmt::{Debug, Display},
3    io::{self},
4    string::FromUtf8Error,
5};
6use thiserror::Error;
7
8type FallbackError = Box<dyn std::error::Error + Send + Sync>;
9
10#[derive(Debug, Error)]
11pub enum Error {
12    #[error("parse failed: {0}")]
13    ParseFailed(FallbackError),
14
15    #[error("io error: {0}")]
16    IOError(std::io::Error),
17
18    /// If you encounter this error, please consider filing a bug on github
19    #[error("unrecognized file format")]
20    UnrecognizedFileFormat,
21}
22
23#[derive(Debug, Error)]
24pub(crate) enum ParsedError {
25    #[error("no enough bytes")]
26    NoEnoughBytes,
27
28    #[error("io error: {0}")]
29    IOError(std::io::Error),
30
31    #[error("{0}")]
32    Failed(String),
33}
34
35/// Due to the fact that metadata in MOV files is typically located at the end
36/// of the file, conventional parsing methods would require reading a
37/// significant amount of unnecessary data during the parsing process. This
38/// would impact the performance of the parsing program and consume more memory.
39///
40/// To address this issue, we have defined an `Error::Skip` enumeration type to
41/// inform the caller that certain bytes in the parsing process are not required
42/// and can be skipped directly. The specific method of skipping can be
43/// determined by the caller based on the situation. For example:
44///
45/// - For files, you can quickly skip using a `Seek` operation.
46///
47/// - For network byte streams, you may need to skip these bytes through read
48///   operations, or preferably, by designing an appropriate network protocol for
49///   skipping.
50///
51/// # [`ParsingError::Skip`]
52///
53/// Please note that when the caller receives an `Error::Skip(n)` error, it
54/// should be understood as follows:
55///
56/// - The parsing program has already consumed all available data and needs to
57///   skip n bytes further.
58///
59/// - After skipping n bytes, it should continue to read subsequent data to fill
60///   the buffer and use it as input for the parsing function.
61///
62/// - The next time the parsing function is called (usually within a loop), the
63///   previously consumed data (including the skipped bytes) should be ignored,
64///   and only the newly read data should be passed in.
65///
66/// # [`ParsingError::Need`]
67///
68/// Additionally, to simplify error handling, we have integrated
69/// `nom::Err::Incomplete` error into `Error::Need`. This allows us to use the
70/// same error type to notify the caller that we require more bytes to continue
71/// parsing.
72#[derive(Debug, Error)]
73pub(crate) enum ParsingError {
74    #[error("need more bytes: {0}")]
75    Need(usize),
76
77    #[error("clear and skip bytes: {0:?}")]
78    ClearAndSkip(usize),
79
80    #[error("{0}")]
81    Failed(String),
82}
83
84#[derive(Debug, Error)]
85pub(crate) struct ParsingErrorState {
86    pub err: ParsingError,
87    pub state: Option<ParsingState>,
88}
89
90impl ParsingErrorState {
91    pub fn new(err: ParsingError, state: Option<ParsingState>) -> Self {
92        Self { err, state }
93    }
94}
95
96impl Display for ParsingErrorState {
97    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98        Display::fmt(
99            &format!(
100                "ParsingError(err: {}, state: {})",
101                self.err,
102                self.state
103                    .as_ref()
104                    .map(|x| x.to_string())
105                    .unwrap_or("None".to_string())
106            ),
107            f,
108        )
109    }
110}
111
112impl From<&str> for ParsingError {
113    fn from(value: &str) -> Self {
114        Self::Failed(value.to_string())
115    }
116}
117
118impl From<std::io::Error> for ParsedError {
119    fn from(value: std::io::Error) -> Self {
120        Self::IOError(value)
121    }
122}
123
124impl From<ParsedError> for crate::Error {
125    fn from(value: ParsedError) -> Self {
126        match value {
127            ParsedError::NoEnoughBytes => Self::ParseFailed(value.into()),
128            ParsedError::IOError(e) => Self::IOError(e),
129            ParsedError::Failed(e) => Self::ParseFailed(e.into()),
130        }
131    }
132}
133
134use Error::*;
135
136use crate::parser::ParsingState;
137
138impl From<io::Error> for Error {
139    fn from(value: io::Error) -> Self {
140        ParseFailed(value.into())
141    }
142}
143
144impl From<String> for Error {
145    fn from(src: String) -> Error {
146        ParseFailed(src.into())
147    }
148}
149
150impl From<&str> for Error {
151    fn from(src: &str) -> Error {
152        src.to_string().into()
153    }
154}
155
156impl From<FromUtf8Error> for Error {
157    fn from(value: FromUtf8Error) -> Self {
158        ParseFailed(value.into())
159    }
160}
161
162impl<T: Debug> From<nom::Err<nom::error::Error<T>>> for crate::Error {
163    fn from(e: nom::Err<nom::error::Error<T>>) -> Self {
164        convert_parse_error(e, "")
165    }
166}
167
168pub(crate) fn convert_parse_error<T: Debug>(
169    e: nom::Err<nom::error::Error<T>>,
170    message: &str,
171) -> Error {
172    let s = match e {
173        nom::Err::Incomplete(_) => format!("{e}; {message}"),
174        nom::Err::Error(e) => format!("{}; {message}", e.code.description()),
175        nom::Err::Failure(e) => format!("{}; {message}", e.code.description()),
176    };
177
178    s.into()
179}
180
181impl From<nom::Err<nom::error::Error<&[u8]>>> for ParsingError {
182    fn from(e: nom::Err<nom::error::Error<&[u8]>>) -> Self {
183        match e {
184            nom::Err::Incomplete(needed) => match needed {
185                nom::Needed::Unknown => ParsingError::Need(1),
186                nom::Needed::Size(n) => ParsingError::Need(n.get()),
187            },
188            nom::Err::Failure(e) | nom::Err::Error(e) => {
189                ParsingError::Failed(e.code.description().to_string())
190            }
191        }
192    }
193}
194
195// impl From<nom::Err<nom::error::Error<&[u8]>>> for ParsingErrorState {
196//     fn from(e: nom::Err<nom::error::Error<&[u8]>>) -> Self {
197//         match e {
198//             nom::Err::Incomplete(needed) => match needed {
199//                 nom::Needed::Unknown => ParsingErrorState::new(ParsingError::Need(1), None),
200//                 nom::Needed::Size(n) => ParsingErrorState::new(ParsingError::Need(n.get()), None),
201//             },
202//             nom::Err::Failure(e) | nom::Err::Error(e) => {
203//                 ParsingErrorState::new(ParsingError::Failed(e.code.description().to_string()), None)
204//             }
205//         }
206//     }
207// }
208
209pub(crate) fn nom_error_to_parsing_error_with_state(
210    e: nom::Err<nom::error::Error<&[u8]>>,
211    state: Option<ParsingState>,
212) -> ParsingErrorState {
213    match e {
214        nom::Err::Incomplete(needed) => match needed {
215            nom::Needed::Unknown => ParsingErrorState::new(ParsingError::Need(1), state),
216            nom::Needed::Size(n) => ParsingErrorState::new(ParsingError::Need(n.get()), state),
217        },
218        nom::Err::Failure(e) | nom::Err::Error(e) => ParsingErrorState::new(
219            ParsingError::Failed(e.code.description().to_string()),
220            state,
221        ),
222    }
223}