Skip to main content

mdf4_rs/
error.rs

1//! Error types for MDF4 operations.
2//!
3//! This module defines the [`Error`] enum which represents all possible failures
4//! that can occur when reading, writing, or processing MDF files.
5//!
6//! # Example
7//!
8//! ```no_run
9//! # #[cfg(feature = "std")]
10//! use mdf4_rs::{MDF, Error, Result};
11//!
12//! # #[cfg(feature = "std")]
13//! fn process_file(path: &str) -> Result<()> {
14//!     match MDF::from_file(path) {
15//!         Ok(mdf) => {
16//!             println!("Loaded {} channel groups", mdf.channel_groups().len());
17//!             Ok(())
18//!         }
19//!         Err(Error::IOError(e)) => {
20//!             eprintln!("File I/O error: {}", e);
21//!             Err(Error::IOError(e))
22//!         }
23//!         Err(Error::FileIdentifierError(id)) => {
24//!             eprintln!("Not a valid MDF file: {}", id);
25//!             Err(Error::FileIdentifierError(id))
26//!         }
27//!         Err(e) => Err(e),
28//!     }
29//! }
30//! ```
31
32use core::fmt;
33
34#[cfg(feature = "alloc")]
35use alloc::string::String;
36
37/// Errors that can occur during MDF file operations.
38///
39/// This enum covers all failure modes including I/O errors, parsing failures,
40/// and structural issues in the MDF file.
41#[derive(Debug)]
42pub enum Error {
43    /// Buffer provided for parsing was too small.
44    ///
45    /// This typically indicates file corruption or an incomplete read.
46    TooShortBuffer {
47        /// Actual number of bytes available
48        actual: usize,
49        /// Minimum number of bytes required
50        expected: usize,
51        /// Source file where the error was detected
52        file: &'static str,
53        /// Line number where the error was detected
54        line: u32,
55    },
56
57    /// The file identifier is not "MDF     " as required by the specification.
58    ///
59    /// This can occur when trying to open a non-MDF file or a file using an
60    /// unsupported variant like "UnFinMF" (unfinalized MDF).
61    FileIdentifierError(String),
62
63    /// The MDF version is not supported (requires 4.1 or later).
64    FileVersioningError(String),
65
66    /// A block identifier did not match the expected value.
67    ///
68    /// Each MDF block starts with a 4-character identifier (e.g., "##HD" for
69    /// the header block). This error indicates structural corruption.
70    BlockIDError {
71        /// The identifier that was found
72        actual: String,
73        /// The identifier that was expected
74        expected: String,
75    },
76
77    /// An I/O error occurred while reading or writing the file.
78    ///
79    /// Only available with the `std` feature.
80    #[cfg(feature = "std")]
81    IOError(std::io::Error),
82
83    /// A write operation failed (no_std version).
84    ///
85    /// Only available without the `std` feature.
86    #[cfg(not(feature = "std"))]
87    WriteError,
88
89    /// The version string in the identification block could not be parsed.
90    InvalidVersionString(String),
91
92    /// Failed to link blocks together during file writing.
93    ///
94    /// This typically indicates a programming error where blocks are
95    /// referenced before being written.
96    BlockLinkError(String),
97
98    /// Failed to serialize a block to bytes.
99    BlockSerializationError(String),
100
101    /// A conversion chain exceeded the maximum allowed depth.
102    ///
103    /// MDF supports chained conversions where one conversion references another.
104    /// This error prevents infinite loops from malformed files.
105    ConversionChainTooDeep {
106        /// The maximum depth that was exceeded
107        max_depth: usize,
108    },
109
110    /// A cycle was detected in a conversion chain.
111    ///
112    /// This indicates file corruption where conversion blocks form a loop.
113    ConversionChainCycle {
114        /// The address where the cycle was detected
115        address: u64,
116    },
117
118    /// A cycle was detected while walking a linked list of blocks (e.g. the
119    /// data-group, channel-group, channel, or data-list chain).
120    ///
121    /// This indicates file corruption where block links form a loop, which
122    /// would otherwise cause the parser to loop forever.
123    LinkCycle {
124        /// The link address that was visited twice
125        address: u64,
126    },
127}
128
129impl fmt::Display for Error {
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131        match self {
132            Error::TooShortBuffer {
133                actual,
134                expected,
135                file,
136                line,
137            } => write!(
138                f,
139                "Buffer too small at {file}:{line}: need at least {expected} bytes, got {actual}"
140            ),
141            Error::FileIdentifierError(id) => {
142                write!(
143                    f,
144                    r#"Invalid file identifier: Expected "MDF     ", found {id}"#
145                )
146            }
147            Error::FileVersioningError(ver) => {
148                write!(f, r#"File version too low: Expected "> 4.1", found {ver}"#)
149            }
150            Error::BlockIDError { actual, expected } => {
151                write!(
152                    f,
153                    "Invalid block identifier: Expected {expected:?}, got {actual:?}"
154                )
155            }
156            #[cfg(feature = "std")]
157            Error::IOError(e) => write!(f, "I/O error: {e}"),
158            #[cfg(not(feature = "std"))]
159            Error::WriteError => write!(f, "Write error"),
160            Error::InvalidVersionString(s) => write!(f, "Invalid version string: {s}"),
161            Error::BlockLinkError(s) => write!(f, "Block linking error: {s}"),
162            Error::BlockSerializationError(s) => write!(f, "Block serialization error: {s}"),
163            Error::ConversionChainTooDeep { max_depth } => {
164                write!(
165                    f,
166                    "Conversion chain too deep: maximum depth of {max_depth} exceeded"
167                )
168            }
169            Error::ConversionChainCycle { address } => {
170                write!(
171                    f,
172                    "Conversion chain cycle detected at block address {address:#x}"
173                )
174            }
175            Error::LinkCycle { address } => {
176                write!(f, "Block link cycle detected at address {address:#x}")
177            }
178        }
179    }
180}
181
182#[cfg(feature = "std")]
183impl std::error::Error for Error {
184    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
185        match self {
186            Error::IOError(e) => Some(e),
187            _ => None,
188        }
189    }
190}
191
192#[cfg(feature = "std")]
193impl From<std::io::Error> for Error {
194    fn from(err: std::io::Error) -> Self {
195        Error::IOError(err)
196    }
197}
198
199/// A specialized Result type for MDF operations.
200///
201/// This is defined as `core::result::Result<T, Error>` for convenience.
202pub type Result<T> = core::result::Result<T, Error>;