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
119impl fmt::Display for Error {
120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121 match self {
122 Error::TooShortBuffer {
123 actual,
124 expected,
125 file,
126 line,
127 } => write!(
128 f,
129 "Buffer too small at {file}:{line}: need at least {expected} bytes, got {actual}"
130 ),
131 Error::FileIdentifierError(id) => {
132 write!(
133 f,
134 r#"Invalid file identifier: Expected "MDF ", found {id}"#
135 )
136 }
137 Error::FileVersioningError(ver) => {
138 write!(f, r#"File version too low: Expected "> 4.1", found {ver}"#)
139 }
140 Error::BlockIDError { actual, expected } => {
141 write!(
142 f,
143 "Invalid block identifier: Expected {expected:?}, got {actual:?}"
144 )
145 }
146 #[cfg(feature = "std")]
147 Error::IOError(e) => write!(f, "I/O error: {e}"),
148 #[cfg(not(feature = "std"))]
149 Error::WriteError => write!(f, "Write error"),
150 Error::InvalidVersionString(s) => write!(f, "Invalid version string: {s}"),
151 Error::BlockLinkError(s) => write!(f, "Block linking error: {s}"),
152 Error::BlockSerializationError(s) => write!(f, "Block serialization error: {s}"),
153 Error::ConversionChainTooDeep { max_depth } => {
154 write!(
155 f,
156 "Conversion chain too deep: maximum depth of {max_depth} exceeded"
157 )
158 }
159 Error::ConversionChainCycle { address } => {
160 write!(
161 f,
162 "Conversion chain cycle detected at block address {address:#x}"
163 )
164 }
165 }
166 }
167}
168
169#[cfg(feature = "std")]
170impl std::error::Error for Error {
171 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
172 match self {
173 Error::IOError(e) => Some(e),
174 _ => None,
175 }
176 }
177}
178
179#[cfg(feature = "std")]
180impl From<std::io::Error> for Error {
181 fn from(err: std::io::Error) -> Self {
182 Error::IOError(err)
183 }
184}
185
186/// A specialized Result type for MDF operations.
187///
188/// This is defined as `core::result::Result<T, Error>` for convenience.
189pub type Result<T> = core::result::Result<T, Error>;