use std::fmt;
#[derive(Debug)]
pub enum GedcomError {
ParseError {
line: u32,
message: String,
},
InvalidFormat(String),
EncodingError(String),
InvalidTag {
line: usize,
tag: String,
},
UnexpectedLevel {
line: usize,
expected: u8,
found: u8,
},
MissingRequiredValue {
line: usize,
tag: String,
},
InvalidValueFormat {
line: usize,
value: String,
expected_format: String,
},
FileSizeLimitExceeded {
size: usize,
max_size: usize,
},
IoError(String),
}
impl fmt::Display for GedcomError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
GedcomError::ParseError { line, message } => {
write!(f, "Parse error at line {line}: {message}")
}
GedcomError::InvalidFormat(msg) => write!(f, "Invalid GEDCOM format: {msg}"),
GedcomError::EncodingError(msg) => write!(f, "Encoding error: {msg}"),
GedcomError::InvalidTag { line, tag } => {
write!(f, "Invalid tag at line {line}: '{tag}'")
}
GedcomError::UnexpectedLevel {
line,
expected,
found,
} => {
write!(
f,
"Unexpected level at line {line}: expected {expected}, found {found}"
)
}
GedcomError::MissingRequiredValue { line, tag } => {
write!(f, "Missing required value for tag '{tag}' at line {line}")
}
GedcomError::InvalidValueFormat {
line,
value,
expected_format,
} => {
write!(
f,
"Invalid value format at line {line}: '{value}' (expected {expected_format})"
)
}
GedcomError::FileSizeLimitExceeded { size, max_size } => {
write!(
f,
"File size limit exceeded: {size} bytes (max: {max_size} bytes)"
)
}
GedcomError::IoError(msg) => write!(f, "I/O error: {msg}"),
}
}
}
impl std::error::Error for GedcomError {}
impl From<std::io::Error> for GedcomError {
fn from(err: std::io::Error) -> Self {
GedcomError::IoError(err.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_error_display() {
let err = GedcomError::ParseError {
line: 10,
message: "Unexpected token".to_string(),
};
assert_eq!(format!("{err}"), "Parse error at line 10: Unexpected token");
}
#[test]
fn test_invalid_format_display() {
let err = GedcomError::InvalidFormat("Missing header".to_string());
assert_eq!(format!("{err}"), "Invalid GEDCOM format: Missing header");
}
#[test]
fn test_encoding_error_display() {
let err = GedcomError::EncodingError("Invalid UTF-8 sequence".to_string());
assert_eq!(format!("{err}"), "Encoding error: Invalid UTF-8 sequence");
}
#[test]
fn test_invalid_tag_display() {
let err = GedcomError::InvalidTag {
line: 15,
tag: "BADTAG".to_string(),
};
assert_eq!(format!("{err}"), "Invalid tag at line 15: 'BADTAG'");
}
#[test]
fn test_unexpected_level_display() {
let err = GedcomError::UnexpectedLevel {
line: 20,
expected: 1,
found: 3,
};
assert_eq!(
format!("{err}"),
"Unexpected level at line 20: expected 1, found 3"
);
}
#[test]
fn test_missing_required_value_display() {
let err = GedcomError::MissingRequiredValue {
line: 25,
tag: "NAME".to_string(),
};
assert_eq!(
format!("{err}"),
"Missing required value for tag 'NAME' at line 25"
);
}
#[test]
fn test_invalid_value_format_display() {
let err = GedcomError::InvalidValueFormat {
line: 30,
value: "not-a-date".to_string(),
expected_format: "DD MMM YYYY".to_string(),
};
assert_eq!(
format!("{err}"),
"Invalid value format at line 30: 'not-a-date' (expected DD MMM YYYY)"
);
}
#[test]
fn test_file_size_limit_exceeded_display() {
let err = GedcomError::FileSizeLimitExceeded {
size: 10_000_000,
max_size: 5_000_000,
};
assert_eq!(
format!("{err}"),
"File size limit exceeded: 10000000 bytes (max: 5000000 bytes)"
);
}
#[test]
fn test_io_error_display() {
let err = GedcomError::IoError("File not found".to_string());
assert_eq!(format!("{err}"), "I/O error: File not found");
}
#[test]
fn test_error_trait_implementation() {
let err: Box<dyn std::error::Error> = Box::new(GedcomError::ParseError {
line: 1,
message: "test".to_string(),
});
assert!(err.to_string().contains("Parse error"));
}
#[test]
fn test_io_error_conversion() {
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let gedcom_err: GedcomError = io_err.into();
match gedcom_err {
GedcomError::IoError(msg) => assert!(msg.contains("not found")),
_ => panic!("Expected IoError variant"),
}
}
}