use alloc::{format, string::String};
use core::fmt;
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(feature = "std")]
use thiserror::Error;
#[cfg_attr(feature = "std", derive(Error))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CoreError {
Parse(crate::parser::ParseError),
Tokenization(String),
Analysis(String),
Plugin(String),
InvalidColor(String),
InvalidNumeric(String),
InvalidTime(String),
Utf8Error { position: usize, message: String },
Io(String),
OutOfMemory(String),
Config(String),
Validation(String),
FeatureNotSupported {
feature: String,
required_feature: String,
},
VersionIncompatible { message: String },
ResourceLimitExceeded {
resource: String,
current: usize,
limit: usize,
},
SecurityViolation(String),
Internal(String),
}
impl CoreError {
pub fn parse<T: fmt::Display>(message: T) -> Self {
Self::Parse(crate::parser::ParseError::IoError {
message: format!("{message}"),
})
}
pub fn internal<T: fmt::Display>(message: T) -> Self {
Self::Internal(format!("{message}"))
}
#[must_use]
pub const fn is_recoverable(&self) -> bool {
match self {
Self::Parse(parse_err) => !matches!(
parse_err,
crate::parser::ParseError::OutOfMemory { .. }
| crate::parser::ParseError::InputTooLarge { .. }
| crate::parser::ParseError::InternalError { .. }
),
Self::Tokenization(_)
| Self::InvalidColor(_)
| Self::InvalidNumeric(_)
| Self::InvalidTime(_)
| Self::Validation(_)
| Self::Analysis(_)
| Self::Plugin(_)
| Self::Utf8Error { .. }
| Self::Io(_)
| Self::Config(_)
| Self::FeatureNotSupported { .. }
| Self::VersionIncompatible { .. } => true,
Self::OutOfMemory(_)
| Self::ResourceLimitExceeded { .. }
| Self::SecurityViolation(_)
| Self::Internal(_) => false,
}
}
#[must_use]
pub const fn is_internal_bug(&self) -> bool {
matches!(self, Self::Internal(_))
}
#[must_use]
pub const fn as_parse_error(&self) -> Option<&crate::parser::ParseError> {
match self {
Self::Parse(parse_err) => Some(parse_err),
_ => None,
}
}
#[must_use]
pub const fn line_number(&self) -> Option<usize> {
match self {
Self::Parse(
crate::parser::ParseError::ExpectedSectionHeader { line }
| crate::parser::ParseError::UnclosedSectionHeader { line }
| crate::parser::ParseError::UnknownSection { line, .. }
| crate::parser::ParseError::InvalidFieldFormat { line }
| crate::parser::ParseError::InvalidFormatLine { line, .. }
| crate::parser::ParseError::FieldCountMismatch { line, .. }
| crate::parser::ParseError::InvalidTimeFormat { line, .. }
| crate::parser::ParseError::InvalidColorFormat { line, .. }
| crate::parser::ParseError::InvalidNumericValue { line, .. }
| crate::parser::ParseError::InvalidStyleOverride { line, .. }
| crate::parser::ParseError::InvalidDrawingCommand { line, .. }
| crate::parser::ParseError::UuDecodeError { line, .. }
| crate::parser::ParseError::MaxNestingDepth { line, .. }
| crate::parser::ParseError::InternalError { line, .. },
) => Some(*line),
Self::Utf8Error { position, .. } => Some(*position),
_ => None,
}
}
#[must_use]
pub fn is_parse_error_type(&self, error_type: &str) -> bool {
match self {
Self::Parse(parse_err) => matches!(
(error_type, parse_err),
(
"section_header",
crate::parser::ParseError::ExpectedSectionHeader { .. }
) | (
"unclosed_header",
crate::parser::ParseError::UnclosedSectionHeader { .. }
) | (
"unknown_section",
crate::parser::ParseError::UnknownSection { .. }
) | (
"field_format",
crate::parser::ParseError::InvalidFieldFormat { .. }
| crate::parser::ParseError::FieldCountMismatch { .. }
) | (
"time_format",
crate::parser::ParseError::InvalidTimeFormat { .. }
) | (
"color_format",
crate::parser::ParseError::InvalidColorFormat { .. }
) | (
"numeric_value",
crate::parser::ParseError::InvalidNumericValue { .. }
) | ("utf8", crate::parser::ParseError::Utf8Error { .. })
),
_ => false,
}
}
}
pub type Result<T> = core::result::Result<T, CoreError>;
#[cfg(not(feature = "std"))]
impl fmt::Display for CoreError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Parse(parse_err) => write!(f, "Parse error: {parse_err}"),
Self::Tokenization(msg) => write!(f, "Tokenization error: {msg}"),
Self::Analysis(msg) => write!(f, "Analysis error: {msg}"),
Self::Plugin(msg) => write!(f, "Plugin error: {msg}"),
Self::InvalidColor(msg) => write!(f, "Invalid color format: {msg}"),
Self::InvalidNumeric(msg) => write!(f, "Invalid numeric value: {msg}"),
Self::InvalidTime(msg) => write!(f, "Invalid time format: {msg}"),
Self::Utf8Error { position, message } => {
write!(f, "UTF-8 encoding error at position {position}: {message}")
}
Self::Io(msg) => write!(f, "I/O error: {msg}"),
Self::OutOfMemory(msg) => write!(f, "Memory allocation failed: {msg}"),
Self::Config(msg) => write!(f, "Configuration error: {msg}"),
Self::Validation(msg) => write!(f, "Validation error: {msg}"),
Self::FeatureNotSupported {
feature,
required_feature,
} => {
write!(
f,
"Feature not supported: {feature} (requires feature '{required_feature}')"
)
}
Self::VersionIncompatible { message } => {
write!(f, "Version incompatibility: {message}")
}
Self::ResourceLimitExceeded {
resource,
current,
limit,
} => {
write!(f, "Resource limit exceeded: {resource} ({current}/{limit})")
}
Self::SecurityViolation(msg) => write!(f, "Security policy violation: {msg}"),
Self::Internal(msg) => {
write!(f, "Internal error: {msg} (this is a bug, please report)")
}
}
}
}
#[cfg(not(feature = "std"))]
impl core::error::Error for CoreError {}
#[cfg(feature = "std")]
impl fmt::Display for CoreError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Parse(parse_err) => write!(f, "Parse error: {parse_err}"),
Self::Tokenization(msg) => write!(f, "Tokenization error: {msg}"),
Self::Analysis(msg) => write!(f, "Analysis error: {msg}"),
Self::Plugin(msg) => write!(f, "Plugin error: {msg}"),
Self::InvalidColor(msg) => write!(f, "Invalid color format: {msg}"),
Self::InvalidNumeric(msg) => write!(f, "Invalid numeric value: {msg}"),
Self::InvalidTime(msg) => write!(f, "Invalid time format: {msg}"),
Self::Utf8Error { position, message } => {
write!(f, "UTF-8 encoding error at position {position}: {message}")
}
Self::Io(msg) => write!(f, "I/O error: {msg}"),
Self::OutOfMemory(msg) => write!(f, "Memory allocation failed: {msg}"),
Self::Config(msg) => write!(f, "Configuration error: {msg}"),
Self::Validation(msg) => write!(f, "Validation error: {msg}"),
Self::FeatureNotSupported {
feature,
required_feature,
} => {
write!(
f,
"Feature not supported: {feature} (requires feature '{required_feature}')"
)
}
Self::VersionIncompatible { message } => {
write!(f, "Version incompatibility: {message}")
}
Self::ResourceLimitExceeded {
resource,
current,
limit,
} => {
write!(f, "Resource limit exceeded: {resource} ({current}/{limit})")
}
Self::SecurityViolation(msg) => write!(f, "Security policy violation: {msg}"),
Self::Internal(msg) => {
write!(f, "Internal error: {msg} (this is a bug, please report)")
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn error_creation() {
let parse_err = CoreError::parse("test message");
assert!(matches!(parse_err, CoreError::Parse(_)));
}
#[test]
fn internal_error() {
let internal_err = CoreError::internal("something went wrong");
assert!(matches!(internal_err, CoreError::Internal(_)));
assert!(internal_err.is_internal_bug());
assert!(!internal_err.is_recoverable());
}
#[test]
fn error_recoverability() {
assert!(CoreError::parse("test").is_recoverable());
assert!(!CoreError::internal("test").is_recoverable());
}
#[test]
fn internal_bug_detection() {
assert!(CoreError::internal("test").is_internal_bug());
assert!(!CoreError::parse("test").is_internal_bug());
}
}