#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("No match")]
NoMatch,
#[error("I/O Error")]
IO(#[from] std::io::Error),
}
pub type ParseResult<T> = std::result::Result<T, Error>;
#[cfg(test)]
mod tests {
use super::*;
use std::io::{Error as IoError, ErrorKind};
#[test]
fn test_error_no_match_display() {
let error = Error::NoMatch;
assert_eq!(error.to_string(), "No match");
}
#[test]
fn test_error_io_display() {
let io_error = IoError::new(ErrorKind::UnexpectedEof, "test error");
let error = Error::IO(io_error);
assert_eq!(error.to_string(), "I/O Error");
}
#[test]
fn test_error_from_io_error() {
let io_error = IoError::new(ErrorKind::PermissionDenied, "access denied");
let error: Error = io_error.into();
match error {
Error::IO(inner) => {
assert_eq!(inner.kind(), ErrorKind::PermissionDenied);
assert_eq!(inner.to_string(), "access denied");
}
_ => panic!("Expected IO error"),
}
}
#[test]
fn test_error_debug_format() {
let error = Error::NoMatch;
let debug_str = format!("{error:?}");
assert_eq!(debug_str, "NoMatch");
let io_error = IoError::new(ErrorKind::NotFound, "file not found");
let error = Error::IO(io_error);
let debug_str = format!("{error:?}");
assert!(debug_str.starts_with("IO("));
assert!(debug_str.contains("NotFound"));
}
#[test]
#[allow(clippy::unnecessary_literal_unwrap)]
fn test_parse_result_ok() {
let result: ParseResult<String> = Ok("success".to_string());
assert!(result.is_ok());
assert_eq!(result.unwrap(), "success");
}
#[test]
#[allow(clippy::unnecessary_literal_unwrap)]
fn test_parse_result_err_no_match() {
let result: ParseResult<String> = Err(Error::NoMatch);
assert!(result.is_err());
match result.unwrap_err() {
Error::NoMatch => (),
_ => panic!("Expected NoMatch error"),
}
}
#[test]
#[allow(clippy::unnecessary_literal_unwrap)]
fn test_parse_result_err_io() {
let io_error = IoError::new(ErrorKind::InvalidData, "corrupt data");
let result: ParseResult<String> = Err(Error::IO(io_error));
assert!(result.is_err());
match result.unwrap_err() {
Error::IO(inner) => {
assert_eq!(inner.kind(), ErrorKind::InvalidData);
assert_eq!(inner.to_string(), "corrupt data");
}
_ => panic!("Expected IO error"),
}
}
#[test]
#[allow(clippy::unnecessary_literal_unwrap)]
fn test_error_chain() {
let io_error = IoError::new(ErrorKind::TimedOut, "operation timed out");
let error = Error::from(io_error);
let result: ParseResult<()> = Err(error);
assert!(result.is_err());
let error = result.unwrap_err();
if let Error::IO(inner) = error {
assert_eq!(inner.kind(), ErrorKind::TimedOut);
} else {
panic!("Expected IO error variant");
}
}
#[test]
fn test_different_io_error_kinds() {
let error_kinds = vec![
ErrorKind::NotFound,
ErrorKind::PermissionDenied,
ErrorKind::ConnectionRefused,
ErrorKind::InvalidInput,
ErrorKind::InvalidData,
ErrorKind::TimedOut,
ErrorKind::Interrupted,
ErrorKind::UnexpectedEof,
];
for kind in error_kinds {
let io_error = IoError::new(kind, format!("test error: {kind:?}"));
let error = Error::from(io_error);
match error {
Error::IO(inner) => assert_eq!(inner.kind(), kind),
_ => panic!("Expected IO error for kind {kind:?}"),
}
}
}
#[test]
fn test_error_equality() {
assert!(matches!(Error::NoMatch, Error::NoMatch));
let io1 = Error::IO(IoError::new(ErrorKind::NotFound, "test1"));
let io2 = Error::IO(IoError::new(ErrorKind::NotFound, "test2"));
match (&io1, &io2) {
(Error::IO(_), Error::IO(_)) => (), _ => panic!("Both should be IO errors"),
}
}
#[test]
fn test_error_send_sync() {
fn assert_send<T: Send>(_: T) {}
fn assert_sync<T: Sync>(_: T) {}
let error = Error::NoMatch;
assert_send(error);
let error = Error::NoMatch;
assert_sync(error);
let io_error = IoError::other("test");
let error = Error::IO(io_error);
assert_send(error);
let io_error = IoError::other("test");
let error = Error::IO(io_error);
assert_sync(error);
}
}