Skip to main content

molrs/
error.rs

1//! Unified error types for the molrs library.
2
3use std::fmt;
4use std::io;
5
6use crate::block::BlockError;
7
8/// Main error type for the molrs library.
9#[derive(Debug)]
10pub enum MolRsError {
11    /// Error from Block operations
12    Block(BlockError),
13
14    /// IO error (file reading/writing)
15    Io(io::Error),
16
17    /// Parse error with context
18    Parse {
19        /// Line number where error occurred (if applicable)
20        line: Option<usize>,
21        /// Error message
22        message: String,
23    },
24
25    /// Validation error
26    Validation {
27        /// Error message
28        message: String,
29    },
30
31    /// Zarr I/O error (only with "zarr" feature)
32    #[cfg(feature = "zarr")]
33    Zarr {
34        /// Error message
35        message: String,
36    },
37
38    /// Entity not found (atom, bond, angle, dihedral)
39    NotFound {
40        /// Kind of entity ("atom", "bond", "angle", "dihedral")
41        entity: &'static str,
42        /// Human-readable message
43        message: String,
44    },
45
46    /// Generic error with message
47    Other(String),
48}
49
50impl fmt::Display for MolRsError {
51    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52        match self {
53            MolRsError::Block(e) => write!(f, "Block error: {}", e),
54            MolRsError::Io(e) => write!(f, "IO error: {}", e),
55            MolRsError::Parse {
56                line: Some(line),
57                message,
58            } => {
59                write!(f, "Parse error at line {}: {}", line, message)
60            }
61            MolRsError::Parse {
62                line: None,
63                message,
64            } => {
65                write!(f, "Parse error: {}", message)
66            }
67            MolRsError::Validation { message } => {
68                write!(f, "Validation error: {}", message)
69            }
70            MolRsError::NotFound { entity, message } => {
71                write!(f, "{} not found: {}", entity, message)
72            }
73            #[cfg(feature = "zarr")]
74            MolRsError::Zarr { message } => {
75                write!(f, "Zarr error: {}", message)
76            }
77            MolRsError::Other(msg) => write!(f, "{}", msg),
78        }
79    }
80}
81
82impl std::error::Error for MolRsError {
83    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
84        match self {
85            MolRsError::Block(e) => Some(e),
86            MolRsError::Io(e) => Some(e),
87            _ => None,
88        }
89    }
90}
91
92// Automatic conversions
93impl From<BlockError> for MolRsError {
94    fn from(err: BlockError) -> Self {
95        MolRsError::Block(err)
96    }
97}
98
99impl From<io::Error> for MolRsError {
100    fn from(err: io::Error) -> Self {
101        MolRsError::Io(err)
102    }
103}
104
105impl From<String> for MolRsError {
106    fn from(msg: String) -> Self {
107        MolRsError::Other(msg)
108    }
109}
110
111impl From<&str> for MolRsError {
112    fn from(msg: &str) -> Self {
113        MolRsError::Other(msg.to_string())
114    }
115}
116
117// Helper constructors
118impl MolRsError {
119    /// Create a parse error with line number
120    pub fn parse_error(line: usize, message: impl Into<String>) -> Self {
121        MolRsError::Parse {
122            line: Some(line),
123            message: message.into(),
124        }
125    }
126
127    /// Create a parse error without line number
128    pub fn parse(message: impl Into<String>) -> Self {
129        MolRsError::Parse {
130            line: None,
131            message: message.into(),
132        }
133    }
134
135    /// Create a validation error
136    pub fn validation(message: impl Into<String>) -> Self {
137        MolRsError::Validation {
138            message: message.into(),
139        }
140    }
141
142    /// Create a not-found error
143    pub fn not_found(entity: &'static str, message: impl Into<String>) -> Self {
144        MolRsError::NotFound {
145            entity,
146            message: message.into(),
147        }
148    }
149
150    /// Create a Zarr I/O error
151    #[cfg(feature = "zarr")]
152    pub fn zarr(message: impl Into<String>) -> Self {
153        MolRsError::Zarr {
154            message: message.into(),
155        }
156    }
157}
158
159#[cfg(test)]
160mod tests {
161    use super::*;
162    use crate::block::BlockError;
163
164    #[test]
165    fn test_error_display() {
166        let err = MolRsError::parse_error(42, "unexpected token");
167        assert_eq!(
168            format!("{}", err),
169            "Parse error at line 42: unexpected token"
170        );
171
172        let err = MolRsError::parse("invalid format");
173        assert_eq!(format!("{}", err), "Parse error: invalid format");
174
175        let err = MolRsError::validation("inconsistent dimensions");
176        assert_eq!(
177            format!("{}", err),
178            "Validation error: inconsistent dimensions"
179        );
180    }
181
182    #[test]
183    fn test_from_block_error() {
184        let block_err = BlockError::RankZero {
185            key: "test".to_string(),
186        };
187        let err: MolRsError = block_err.into();
188        assert!(matches!(err, MolRsError::Block(_)));
189    }
190
191    #[test]
192    fn test_from_string() {
193        let err: MolRsError = "test error".into();
194        assert!(matches!(err, MolRsError::Other(_)));
195        assert_eq!(format!("{}", err), "test error");
196    }
197}