//! Error types and result handling for parsing operations. //! //! This module defines the core error types used throughout the parsing library. //! The [`Error`] enum distinguishes between expected parsing failures (`NoMatch`) //! that are part of normal backtracking, and unexpected I/O errors from the //! underlying data source. //! //! The [`ParseResult`] type alias provides a convenient shorthand for //! `Result` used by all parsing operations. #[derive(Debug, thiserror::Error)] pub enum Error { #[error("No match")] NoMatch, #[error("I/O Error")] IO(#[from] std::io::Error), } pub type ParseResult = std::result::Result; #[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 = 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 = 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 = 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() { // Test that NoMatch errors are equal assert!(matches!(Error::NoMatch, Error::NoMatch)); // Note: IO errors don't implement PartialEq, so we can't test equality // But we can test that they're both IO variants 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(_)) => (), // Both are IO errors _ => panic!("Both should be IO errors"), } } #[test] fn test_error_send_sync() { fn assert_send(_: T) {} fn assert_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); } }