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
//! Error types and internal error handling functions
use super::*;

/// An error while parsing a BinRead type
#[non_exhaustive]
pub enum Error {
    /// The magic value did not match the provided one
    BadMagic {
        // Position in number of bytes from the start of the reader
        pos: usize,
        // The value found. Use [`Any::downcast_ref`](core::any::Any::downcast_ref) to access
        found: Box<dyn Any>
    },
    /// The condition of an assertion without a custom type failed
    AssertFail {
        pos: usize,
        message: String
    },
    /// An error that occured while reading from, or seeking within, the reader
    Io(io::Error),
    /// A custom error, most often given from the second value passed into an [`assert`](attribute#Assert)
    Custom {
        pos: usize,
        err: Box<dyn Any>
    },
    /// No variant in the enum was successful in parsing the data
    NoVariantMatch {
        pos: usize
    },
    EnumErrors {
        pos: usize,
        variant_errors: Vec<(/*variant name*/ &'static str, Error)>,
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Self {
        Self::Io(err)
    }
}

#[cfg(feature = "std")]
impl std::error::Error for Error {}

use core::fmt;

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::BadMagic { pos, .. } => write!(f, "BadMagic {{ pos: 0x{:X} }}", pos),
            Self::AssertFail { pos, message } => write!(f, "AssertFail at 0x{:X}: \"{}\"", pos, message),
            Self::Io(err) => write!(f, "Io({:?})", err),
            Self::Custom { pos, err } => write!(f, "Custom {{ pos: 0x{:X}, err: {:?} }}", pos, err),
            _ => write!(f, "EnumErrors")
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::BadMagic { pos, .. } => write!(f, "BadMagic {{ pos: 0x{:X} }}", pos),
            Self::AssertFail { pos, message } => write!(f, "AssertFail at 0x{:X}: \"{}\"", pos, message),
            Self::Io(err) => write!(f, "Io({:?})", err),
            Self::Custom { pos, err } => write!(f, "Custom {{ pos: 0x{:X}, err: {:?} }}", pos, err),
            _ => write!(f, "EnumErrors")
        }
    }
}

/// Read a value then check if it is the expected value
pub fn magic<R, B>(reader: &mut R, expected: B, options: &ReadOptions) -> BinResult<()>
    where B: BinRead<Args=()> + PartialEq + 'static,
          R: io::Read + io::Seek
{
    let pos = reader.seek(SeekFrom::Current(0))?;
    #[cfg(feature = "debug_template")]
    let mut options = options.clone();
    #[cfg(feature = "debug_template")] {
        options.variable_name = Some("magic");
    }
    let val = B::read_options(reader, &options, ())?;
    if val == expected {
        Ok(())
    } else {
        Err(Error::BadMagic {
            pos: pos as usize,
            found: Box::new(val) as _
        })
    }
}

// pub fn assert_eq<R, B, E, A>(reader: &mut R, expected: B, error: Option<E>) -> BinResult<()>
//     where B: BinRead<Args=()> + std::fmt::Debug + PartialEq,
//           R: io::Read + io::Seek,
//           A: core::fmt::Debug + 'static,
//           E: Fn() -> A,
// {
//     let pos = reader.seek(SeekFrom::Current(0))? as usize;
//     let val = B::read(reader)?;
//     if val == expected {
//         Ok(())
//     } else {
//         error.map(|err|{
//             Err(Error::Custom {
//                 pos,
//                 err: Box::new(err())
//             })
//         }).unwrap_or_else(||{
//             Err(Error::AssertFail {
//                 pos,
//                 message: "Assertion failed".into()
//             })
//         })
//     }
// }

/// Assert a condition is true and if not optionally apply a function to generate the error
pub fn assert<R, E, A>(reader: &mut R, test: bool, message: &str, error: Option<E>) -> BinResult<()>
    where R: io::Read + io::Seek,
          A: core::fmt::Debug + 'static,
          E: Fn() -> A,
{
    let pos = reader.seek(SeekFrom::Current(0))? as usize;
    if test {
        Ok(())
    } else {
        error.map(|err|{
            Err(Error::Custom {
                pos,
                err: Box::new(err())
            })
        }).unwrap_or_else(||{
            Err(Error::AssertFail {
                pos,
                message: message.into()
            })
        })
    }
}

#[doc(hidden)]
/// A no-op replacement for [`BinRead::read_options`](BinRead::read_options) that returns the unit type
/// 
/// **Intended for internal use only**
pub fn nop3<T1, R: Read + Seek>(_: &mut R, _: &ReadOptions, _: T1) -> BinResult<()> {
    Ok(())
}

#[doc(hidden)]
/// A no-op replacement for [`BinRead::read_options`](BinRead::read_options) that returns the
/// default value for the given type. Internally used for the `default` attribute.
/// 
/// **Intended for internal use only**
pub fn nop3_default<T1, R: Read + Seek, D: Default>(_: &mut R, _: &ReadOptions, _: T1) -> BinResult<D> {
    Ok(D::default())
}

#[doc(hidden)]
/// A no-op replacement for [`BinRead::after_parse`](BinRead::after_parse)
/// 
/// **Intended for internal use only**
pub fn nop5<T1, T2, R: Read + Seek>(_: &mut T1, _: &mut R, _: &ReadOptions, _: T2) -> BinResult<()> {
    Ok(())
}

#[doc(hidden)]
/// Functional wrapper to apply a [`BinRead::after_parse`](BinRead::after_parse) stand-in function
/// to a value and then return the value if the [`after_parse`](BinRead::after_parse) function succeeds.
///
/// Used by the derive macro to optionally immediately dereference/postprocess the value when
/// first parsed. In theory should be optimized out in case of no-op.
/// 
/// **Intended for internal use only**
pub fn identity_after_parse<PostprocessFn, Reader, ValueType, ArgType>(
    after_parse_fn: PostprocessFn,
    mut item: ValueType,
    reader: &mut Reader,
    ro: &ReadOptions,
    args: ArgType,
) -> BinResult<ValueType>
    where Reader: Read + Seek,
          PostprocessFn: Fn(
              &mut ValueType,
              &mut Reader,
              &ReadOptions,
              ArgType,
          ) -> BinResult<()>, 
{
    after_parse_fn(&mut item, reader, ro, args)?;
    Ok(item)
}

/// Conversion used internally by the `try` attribute to make parsing failures softer
pub fn try_conversion<T>(result: BinResult<T>) -> BinResult<Option<T>> {
    Ok(result.ok())
}

pub fn read_options_then_after_parse<Args, T, R>(
    reader: &mut R,
    ro: &ReadOptions,
    args: T::Args,
) -> BinResult<T>
    where Args: Copy + 'static,
          T: BinRead<Args = Args>,
          R: Read + Seek,
{
    let mut val = T::read_options(reader, ro, args.clone())?;
    val.after_parse(reader, ro, args)?;
    Ok(val)
}

impl Error {
    /// Gets a custom error of type T from the Error. Returns `None` if the error type is not
    /// custom or if the contained error is not of the desired type.
    pub fn custom_err<T: Any>(&self) -> Option<&T> {
        if let Error::Custom { err, ..} = self {
            err.downcast_ref()
        } else {
            None
        }
    }
}