use apollo_errors::{Error, FormatConfig};
use miette::Diagnostic;
#[derive(Debug, Error, Diagnostic, Clone)]
#[error("IO error: {message}")]
#[diagnostic(code(source::io))]
pub struct IoError {
#[extension]
pub message: String,
}
#[derive(Debug, Error, Diagnostic, Clone)]
#[error("Parse error: {details}")]
#[diagnostic(code(source::parse))]
pub struct ParseError {
#[extension]
pub details: String,
}
#[derive(Debug, Error, Diagnostic)]
#[allow(dead_code)]
pub enum ErrorWithFrom {
#[error("IO operation failed")]
#[diagnostic(code(from::io_failed))]
Io {
#[from]
source: IoError,
},
#[error(transparent)]
#[diagnostic(transparent)]
Parse(#[from] ParseError),
#[error("Generic error: {message}")]
#[diagnostic(code(from::generic_error))]
Generic {
#[extension]
message: String,
},
}
#[test]
fn test_from_impl_for_field() {
let io_error = IoError {
message: "file not found".to_string(),
};
let error: ErrorWithFrom = io_error.into();
match &error {
ErrorWithFrom::Io { source } => {
assert_eq!(source.message, "file not found");
}
_ => panic!("Expected Io variant"),
}
}
#[test]
fn test_from_impl_for_transparent_variant() {
let parse_error = ParseError {
details: "unexpected token".to_string(),
};
let error: ErrorWithFrom = parse_error.into();
match &error {
ErrorWithFrom::Parse(source) => {
assert_eq!(source.details, "unexpected token");
}
_ => panic!("Expected Parse variant"),
}
}
#[test]
fn test_question_mark_operator_works() {
fn may_fail() -> Result<(), IoError> {
Err(IoError {
message: "disk full".to_string(),
})
}
fn wrapper() -> Result<(), ErrorWithFrom> {
may_fail()?;
Ok(())
}
let result = wrapper();
assert!(result.is_err());
match result.unwrap_err() {
ErrorWithFrom::Io { source } => {
assert_eq!(source.message, "disk full");
}
_ => panic!("Expected Io variant"),
}
}
#[test]
fn test_from_implies_source() {
let io_error = IoError {
message: "permission denied".to_string(),
};
let error: ErrorWithFrom = io_error.into();
let source = std::error::Error::source(&error);
assert!(source.is_some());
assert_eq!(source.unwrap().to_string(), "IO error: permission denied");
}
#[test]
fn test_from_variant_display() {
let error: ErrorWithFrom = IoError {
message: "read failed".to_string(),
}
.into();
assert_eq!(error.to_string(), "IO operation failed");
}
#[test]
fn test_from_variant_json() {
use apollo_errors::Error as ErrorTrait;
let error: ErrorWithFrom = IoError {
message: "write failed".to_string(),
}
.into();
let json = error.to_json(FormatConfig::default()).unwrap();
assert_eq!(json["error"], "from::io_failed");
assert_eq!(json["message"], "IO operation failed");
}
#[derive(Debug, Error, Diagnostic)]
#[error("Wrapped IO error")]
#[diagnostic(code(wrapper::io))]
pub struct WrappedIoError {
#[from]
source: IoError,
}
#[test]
fn test_struct_from_impl() {
let io_error = IoError {
message: "connection reset".to_string(),
};
let error: WrappedIoError = io_error.into();
assert_eq!(error.source.message, "connection reset");
}
#[test]
fn test_struct_from_question_mark() {
fn may_fail() -> Result<(), IoError> {
Err(IoError {
message: "timeout".to_string(),
})
}
fn wrapper() -> Result<(), WrappedIoError> {
may_fail()?;
Ok(())
}
let result = wrapper();
assert!(result.is_err());
assert_eq!(result.unwrap_err().source.message, "timeout");
}
#[test]
fn test_struct_from_implies_source() {
let error: WrappedIoError = IoError {
message: "broken pipe".to_string(),
}
.into();
let source = std::error::Error::source(&error);
assert!(source.is_some());
assert_eq!(source.unwrap().to_string(), "IO error: broken pipe");
}
#[test]
fn test_struct_from_display() {
let error: WrappedIoError = IoError {
message: "any".to_string(),
}
.into();
assert_eq!(error.to_string(), "Wrapped IO error");
}
#[test]
fn test_struct_from_json() {
use apollo_errors::Error as ErrorTrait;
let error: WrappedIoError = IoError {
message: "any".to_string(),
}
.into();
let json = error.to_json(FormatConfig::default()).unwrap();
assert_eq!(json["error"], "wrapper::io");
assert_eq!(json["message"], "Wrapped IO error");
}