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
//! Contains helper structs for error handling

use std::convert::From;
use std::error::Error;
use std::fmt;
use std::io;
use std::num::{ParseFloatError, ParseIntError};

/// A type for results generated by `load_obj` and `load_mtl` where the `Err` type is hard-wired to
/// `ObjError`
///
/// This typedef is generally used to avoid writing out `ObjError` directly and is otherwise a
/// direct mapping to `std::result::Result`.
pub type ObjResult<T> = Result<T, ObjError>;

/// The error type for loading of the `obj` file.
#[derive(Debug)]
pub enum ObjError {
    /// IO error has been occurred during opening the `obj` file.
    Io(io::Error),
    /// Tried to parse integer frome the `obj` file, but failed.
    ParseInt(ParseIntError),
    /// Tried to parse floating point number frome the `obj` file, but failed.
    ParseFloat(ParseFloatError),
    /// `LoadError` has been occurred during parseing the `obj` file.
    Load(LoadError),
}

macro_rules! implmnt {
    ($name:ident, $error:path) => {
        impl From<$error> for ObjError {
            fn from(err: $error) -> Self {
                ObjError::$name(err)
            }
        }
    };
}

impl fmt::Display for ObjError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ObjError::Io(ref e) => e.fmt(f),
            ObjError::ParseInt(ref e) => e.fmt(f),
            ObjError::ParseFloat(ref e) => e.fmt(f),
            ObjError::Load(ref e) => e.fmt(f),
        }
    }
}

impl Error for ObjError {
    fn cause(&self) -> Option<&dyn Error> {
        match *self {
            ObjError::Io(ref err) => Some(err),
            ObjError::ParseInt(ref err) => Some(err),
            ObjError::ParseFloat(ref err) => Some(err),
            ObjError::Load(ref err) => Some(err),
        }
    }
}

implmnt!(Io, io::Error);
implmnt!(ParseInt, ParseIntError);
implmnt!(ParseFloat, ParseFloatError);
implmnt!(Load, LoadError);

/// The error type for parse operations of the `Obj` struct.
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct LoadError {
    kind: LoadErrorKind,
    message: &'static str,
}

impl LoadError {
    /// Outputs the detailed cause of loading an OBJ failing.
    pub fn kind(&self) -> &LoadErrorKind {
        &self.kind
    }
}

/// Enum to store the various types of errors that can cause loading an OBJ to fail.
#[derive(Copy, PartialEq, Eq, Clone, Debug)]
pub enum LoadErrorKind {
    /// Met unexpected statement.
    UnexpectedStatement,
    /// Received wrong number of arguments.
    WrongNumberOfArguments,
    /// Received unexpected type of arguments.
    WrongTypeOfArguments,
    /// Model should be triangulated first to be loaded properly.
    UntriangulatedModel,
    /// Model cannot be transformed into requested form.
    InsufficientData,
    /// Received index value out of range.
    IndexOutOfRange,
}

impl LoadError {
    /// Creates a new custom error from a specified kind and message.
    pub fn new(kind: LoadErrorKind, message: &'static str) -> Self {
        LoadError { kind, message }
    }
}

impl Error for LoadError {}

impl fmt::Display for LoadError {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        use LoadErrorKind::*;

        let msg = match self.kind {
            UnexpectedStatement => "Met unexpected statement",
            WrongNumberOfArguments => "Received wrong number of arguments",
            WrongTypeOfArguments => "Received unexpected type of arguments",
            UntriangulatedModel => "Model should be triangulated first to be loaded properly",
            InsufficientData => "Model cannot be transformed into requested form",
            IndexOutOfRange => "Received index value out of range",
        };

        write!(fmt, "{}: {}", msg, self.message)
    }
}

macro_rules! make_error {
    ($kind:ident, $message:expr) => {
        return Err(::std::convert::From::from($crate::error::LoadError::new(
            $crate::error::LoadErrorKind::$kind,
            $message,
        )));
    };
}