use std::fmt;
use crate::oniguruma::*;
use crate::regerror::onig_error_code_to_format;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RegexError {
Memory,
MatchStackLimitOver,
RetryLimitInMatchOver,
RetryLimitInSearchOver,
SubexpCallLimitOver,
TimeLimitOver,
ParseDepthLimitOver,
Syntax { code: i32, message: String },
InvalidArgument,
InternalBug { code: i32, message: String },
NotInitialized,
Encoding { code: i32, message: String },
Other(i32),
}
impl fmt::Display for RegexError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RegexError::Memory => write!(f, "memory allocation failed"),
RegexError::MatchStackLimitOver => write!(f, "match-stack limit over"),
RegexError::RetryLimitInMatchOver => write!(f, "retry-limit-in-match over"),
RegexError::RetryLimitInSearchOver => write!(f, "retry-limit-in-search over"),
RegexError::SubexpCallLimitOver => write!(f, "subexp-call-limit-in-search over"),
RegexError::TimeLimitOver => write!(f, "time limit over"),
RegexError::ParseDepthLimitOver => write!(f, "parse depth limit over"),
RegexError::Syntax { message, .. } => write!(f, "syntax error: {}", message),
RegexError::InvalidArgument => write!(f, "invalid argument"),
RegexError::InternalBug { message, .. } => write!(f, "internal error: {}", message),
RegexError::NotInitialized => write!(f, "library is not initialized"),
RegexError::Encoding { message, .. } => write!(f, "encoding error: {}", message),
RegexError::Other(code) => write!(f, "error code {}", code),
}
}
}
impl std::error::Error for RegexError {}
impl From<i32> for RegexError {
fn from(code: i32) -> Self {
match code {
ONIGERR_MEMORY => RegexError::Memory,
ONIGERR_MATCH_STACK_LIMIT_OVER => RegexError::MatchStackLimitOver,
ONIGERR_RETRY_LIMIT_IN_MATCH_OVER => RegexError::RetryLimitInMatchOver,
ONIGERR_RETRY_LIMIT_IN_SEARCH_OVER => RegexError::RetryLimitInSearchOver,
ONIGERR_SUBEXP_CALL_LIMIT_IN_SEARCH_OVER => RegexError::SubexpCallLimitOver,
ONIGERR_TIME_LIMIT_OVER => RegexError::TimeLimitOver,
ONIGERR_PARSE_DEPTH_LIMIT_OVER => RegexError::ParseDepthLimitOver,
ONIGERR_INVALID_ARGUMENT => RegexError::InvalidArgument,
ONIGERR_LIBRARY_IS_NOT_INITIALIZED => RegexError::NotInitialized,
ONIGERR_TYPE_BUG
| ONIGERR_PARSER_BUG
| ONIGERR_STACK_BUG
| ONIGERR_UNDEFINED_BYTECODE
| ONIGERR_UNEXPECTED_BYTECODE => RegexError::InternalBug {
code,
message: onig_error_code_to_format(code).to_string(),
},
ONIGERR_DEFAULT_ENCODING_IS_NOT_SET
| ONIGERR_SPECIFIED_ENCODING_CANT_CONVERT_TO_WIDE_CHAR
| ONIGERR_NOT_SUPPORTED_ENCODING_COMBINATION => RegexError::Encoding {
code,
message: onig_error_code_to_format(code).to_string(),
},
c if onig_is_pattern_error(c) => RegexError::Syntax {
code: c,
message: onig_error_code_to_format(c).to_string(),
},
_ => RegexError::Other(code),
}
}
}
impl RegexError {
pub fn code(&self) -> i32 {
match self {
RegexError::Memory => ONIGERR_MEMORY,
RegexError::MatchStackLimitOver => ONIGERR_MATCH_STACK_LIMIT_OVER,
RegexError::RetryLimitInMatchOver => ONIGERR_RETRY_LIMIT_IN_MATCH_OVER,
RegexError::RetryLimitInSearchOver => ONIGERR_RETRY_LIMIT_IN_SEARCH_OVER,
RegexError::SubexpCallLimitOver => ONIGERR_SUBEXP_CALL_LIMIT_IN_SEARCH_OVER,
RegexError::TimeLimitOver => ONIGERR_TIME_LIMIT_OVER,
RegexError::ParseDepthLimitOver => ONIGERR_PARSE_DEPTH_LIMIT_OVER,
RegexError::InvalidArgument => ONIGERR_INVALID_ARGUMENT,
RegexError::NotInitialized => ONIGERR_LIBRARY_IS_NOT_INITIALIZED,
RegexError::Syntax { code, .. } => *code,
RegexError::InternalBug { code, .. } => *code,
RegexError::Encoding { code, .. } => *code,
RegexError::Other(code) => *code,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_memory_error() {
let err = RegexError::from(ONIGERR_MEMORY);
assert!(matches!(err, RegexError::Memory));
assert_eq!(err.code(), ONIGERR_MEMORY);
assert_eq!(err.to_string(), "memory allocation failed");
}
#[test]
fn from_syntax_error() {
let err = RegexError::from(ONIGERR_PREMATURE_END_OF_CHAR_CLASS);
assert!(matches!(err, RegexError::Syntax { .. }));
assert_eq!(err.code(), ONIGERR_PREMATURE_END_OF_CHAR_CLASS);
assert!(err.to_string().contains("syntax error"));
}
#[test]
fn from_internal_bug() {
let err = RegexError::from(ONIGERR_PARSER_BUG);
assert!(matches!(err, RegexError::InternalBug { .. }));
}
#[test]
fn from_encoding_error() {
let err = RegexError::from(ONIGERR_NOT_SUPPORTED_ENCODING_COMBINATION);
assert!(matches!(err, RegexError::Encoding { .. }));
}
#[test]
fn from_unknown_code() {
let err = RegexError::from(-9999);
assert!(matches!(err, RegexError::Other(-9999)));
}
#[test]
fn display_impl() {
let err = RegexError::InvalidArgument;
assert_eq!(format!("{}", err), "invalid argument");
}
#[test]
fn error_trait() {
let err: Box<dyn std::error::Error> = Box::new(RegexError::Memory);
assert_eq!(err.to_string(), "memory allocation failed");
}
#[test]
fn display_all_variants() {
let cases: Vec<(RegexError, &str)> = vec![
(RegexError::Memory, "memory allocation failed"),
(RegexError::MatchStackLimitOver, "match-stack limit over"),
(
RegexError::RetryLimitInMatchOver,
"retry-limit-in-match over",
),
(
RegexError::RetryLimitInSearchOver,
"retry-limit-in-search over",
),
(
RegexError::SubexpCallLimitOver,
"subexp-call-limit-in-search over",
),
(RegexError::TimeLimitOver, "time limit over"),
(RegexError::ParseDepthLimitOver, "parse depth limit over"),
(
RegexError::Syntax {
code: -100,
message: "bad pattern".into(),
},
"syntax error: bad pattern",
),
(RegexError::InvalidArgument, "invalid argument"),
(
RegexError::InternalBug {
code: -1,
message: "oops".into(),
},
"internal error: oops",
),
(RegexError::NotInitialized, "library is not initialized"),
(
RegexError::Encoding {
code: -21,
message: "bad enc".into(),
},
"encoding error: bad enc",
),
(RegexError::Other(-9999), "error code -9999"),
];
for (err, expected) in cases {
assert_eq!(err.to_string(), expected);
}
}
#[test]
fn code_all_variants() {
assert_eq!(RegexError::Memory.code(), ONIGERR_MEMORY);
assert_eq!(
RegexError::MatchStackLimitOver.code(),
ONIGERR_MATCH_STACK_LIMIT_OVER
);
assert_eq!(
RegexError::RetryLimitInMatchOver.code(),
ONIGERR_RETRY_LIMIT_IN_MATCH_OVER
);
assert_eq!(
RegexError::RetryLimitInSearchOver.code(),
ONIGERR_RETRY_LIMIT_IN_SEARCH_OVER
);
assert_eq!(
RegexError::SubexpCallLimitOver.code(),
ONIGERR_SUBEXP_CALL_LIMIT_IN_SEARCH_OVER
);
assert_eq!(RegexError::TimeLimitOver.code(), ONIGERR_TIME_LIMIT_OVER);
assert_eq!(
RegexError::ParseDepthLimitOver.code(),
ONIGERR_PARSE_DEPTH_LIMIT_OVER
);
assert_eq!(RegexError::InvalidArgument.code(), ONIGERR_INVALID_ARGUMENT);
assert_eq!(
RegexError::NotInitialized.code(),
ONIGERR_LIBRARY_IS_NOT_INITIALIZED
);
assert_eq!(
RegexError::Syntax {
code: -100,
message: String::new()
}
.code(),
-100
);
assert_eq!(
RegexError::InternalBug {
code: -5,
message: String::new()
}
.code(),
-5
);
assert_eq!(
RegexError::Encoding {
code: -21,
message: String::new()
}
.code(),
-21
);
assert_eq!(RegexError::Other(-9999).code(), -9999);
}
#[test]
fn from_limit_errors() {
assert!(matches!(
RegexError::from(ONIGERR_MATCH_STACK_LIMIT_OVER),
RegexError::MatchStackLimitOver
));
assert!(matches!(
RegexError::from(ONIGERR_RETRY_LIMIT_IN_MATCH_OVER),
RegexError::RetryLimitInMatchOver
));
assert!(matches!(
RegexError::from(ONIGERR_RETRY_LIMIT_IN_SEARCH_OVER),
RegexError::RetryLimitInSearchOver
));
assert!(matches!(
RegexError::from(ONIGERR_SUBEXP_CALL_LIMIT_IN_SEARCH_OVER),
RegexError::SubexpCallLimitOver
));
assert!(matches!(
RegexError::from(ONIGERR_TIME_LIMIT_OVER),
RegexError::TimeLimitOver
));
assert!(matches!(
RegexError::from(ONIGERR_PARSE_DEPTH_LIMIT_OVER),
RegexError::ParseDepthLimitOver
));
assert!(matches!(
RegexError::from(ONIGERR_INVALID_ARGUMENT),
RegexError::InvalidArgument
));
assert!(matches!(
RegexError::from(ONIGERR_LIBRARY_IS_NOT_INITIALIZED),
RegexError::NotInitialized
));
}
}