obj/
error.rs

1//! Contains helper structs for error handling
2
3use std::error::Error;
4use std::fmt;
5use std::io;
6use std::num::{ParseFloatError, ParseIntError};
7
8/// A type for results generated by `load_obj` and `load_mtl` where the `Err` type is hard-wired to
9/// `ObjError`
10///
11/// This typedef is generally used to avoid writing out `ObjError` directly and is otherwise a
12/// direct mapping to `std::result::Result`.
13pub type ObjResult<T> = Result<T, ObjError>;
14
15/// The error type for loading of the `obj` file.
16#[derive(Debug)]
17pub enum ObjError {
18    /// IO error has been occurred during opening the `obj` file.
19    Io(io::Error),
20    /// Tried to parse integer frome the `obj` file, but failed.
21    ParseInt(ParseIntError),
22    /// Tried to parse floating point number frome the `obj` file, but failed.
23    ParseFloat(ParseFloatError),
24    /// `LoadError` has been occurred during parseing the `obj` file.
25    Load(LoadError),
26}
27
28macro_rules! implmnt {
29    ($name:ident, $error:path) => {
30        impl From<$error> for ObjError {
31            fn from(err: $error) -> Self {
32                ObjError::$name(err)
33            }
34        }
35    };
36}
37
38impl fmt::Display for ObjError {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        match self {
41            ObjError::Io(ref e) => e.fmt(f),
42            ObjError::ParseInt(ref e) => e.fmt(f),
43            ObjError::ParseFloat(ref e) => e.fmt(f),
44            ObjError::Load(ref e) => e.fmt(f),
45        }
46    }
47}
48
49impl Error for ObjError {
50    fn cause(&self) -> Option<&dyn Error> {
51        match *self {
52            ObjError::Io(ref err) => Some(err),
53            ObjError::ParseInt(ref err) => Some(err),
54            ObjError::ParseFloat(ref err) => Some(err),
55            ObjError::Load(ref err) => Some(err),
56        }
57    }
58}
59
60implmnt!(Io, io::Error);
61implmnt!(ParseInt, ParseIntError);
62implmnt!(ParseFloat, ParseFloatError);
63implmnt!(Load, LoadError);
64
65/// The error type for parse operations of the `Obj` struct.
66#[derive(PartialEq, Eq, Clone, Debug)]
67pub struct LoadError {
68    kind: LoadErrorKind,
69    message: String,
70}
71
72impl LoadError {
73    /// Outputs the detailed cause of loading an OBJ failing.
74    pub fn kind(&self) -> &LoadErrorKind {
75        &self.kind
76    }
77}
78
79/// Enum to store the various types of errors that can cause loading an OBJ to fail.
80#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
81pub enum LoadErrorKind {
82    /// Met unexpected statement.
83    UnexpectedStatement,
84    /// Received wrong number of arguments.
85    WrongNumberOfArguments,
86    /// Received unexpected type of arguments.
87    WrongTypeOfArguments,
88    /// Model should be triangulated first to be loaded properly.
89    UntriangulatedModel,
90    /// Model cannot be transformed into requested form.
91    InsufficientData,
92    /// Received index value out of range.
93    IndexOutOfRange,
94    /// A line is expected after the backslash (\).
95    BackslashAtEOF,
96    /// Group number exceeded limitation.
97    TooBigGroupNumber,
98}
99
100impl LoadError {
101    /// Creates a new custom error from a specified kind and message.
102    #[deprecated(
103        since = "0.7.4",
104        note = "You shouldn’t need to create a LoadError instance on your own."
105    )]
106    pub fn new(kind: LoadErrorKind, message: &'static str) -> Self {
107        let message = message.to_string();
108        LoadError { kind, message }
109    }
110
111    pub(crate) fn new_internal(kind: LoadErrorKind, message: String) -> Self {
112        LoadError { kind, message }
113    }
114}
115
116impl Error for LoadError {}
117
118impl fmt::Display for LoadError {
119    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
120        use LoadErrorKind::*;
121
122        let msg = match self.kind {
123            UnexpectedStatement => "Met unexpected statement",
124            WrongNumberOfArguments => "Received wrong number of arguments",
125            WrongTypeOfArguments => "Received unexpected type of arguments",
126            UntriangulatedModel => "Model should be triangulated first to be loaded properly",
127            InsufficientData => "Model cannot be transformed into requested form",
128            IndexOutOfRange => "Received index value out of range",
129            BackslashAtEOF => r"A line is expected after the backslash (\)",
130            TooBigGroupNumber => "Group number exceeded limitation.",
131        };
132
133        write!(fmt, "{}: {}", msg, self.message)
134    }
135}
136
137macro_rules! make_error {
138    ($kind:ident, $message:expr) => {
139        return Err($crate::error::ObjError::Load(
140            $crate::error::LoadError::new_internal(
141                $crate::error::LoadErrorKind::$kind,
142                $message.to_string(),
143            ),
144        ))
145    };
146}
147
148pub(crate) use make_error;
149
150pub(super) fn index_out_of_range<T, I>(index: usize) -> ObjResult<T> {
151    let name = std::any::type_name::<I>();
152    Err(ObjError::Load(LoadError {
153        kind: LoadErrorKind::IndexOutOfRange,
154        message: format!(
155            "Given index type '{name}' is not large enough to contain the index '{index}'"
156        ),
157    }))
158}