use crate::Span;
use std::fmt;
#[derive(Debug)]
pub struct Error {
pub kind: Box<ErrorKind>,
}
#[derive(Debug)]
pub enum ErrorKind {
Io(std::io::Error),
Regex(regex::Error),
Dialoguer(dialoguer::Error),
Parse { message: String, span: Span },
UnexpectedChar { character: char, span: Span },
ExpectedChar { expected: char, found: char, span: Span },
ExpectedString { expected: String, found: String, span: Span },
ExpectedOneOf { expected: Vec<String>, found: String, span: Span },
ExpectedClosingTag { expected: String, found: String, span: Span },
InvalidI18n { format: String, span: Span },
UnsupportedI18nFormat { format: String, span: Span },
NotImplemented { feature: String, span: Span },
ParseFloatError { source: String, span: Span },
UnexpectedContent { message: String, span: Span },
TrailingContent { span: Span },
ExternalError { source: String, details: String, span: Span },
Codegen { message: String, span: Span },
}
impl Error {
pub fn new(kind: ErrorKind) -> Self {
Self { kind: Box::new(kind) }
}
pub fn io_error(err: std::io::Error) -> Self {
Self::new(ErrorKind::Io(err))
}
pub fn unexpected_char(character: char, span: Span) -> Self {
Self::new(ErrorKind::UnexpectedChar { character, span })
}
pub fn expected_char(expected: char, found: char, span: Span) -> Self {
Self::new(ErrorKind::ExpectedChar { expected, found, span })
}
pub fn expected_string(expected: String, found: String, span: Span) -> Self {
Self::new(ErrorKind::ExpectedString { expected, found, span })
}
pub fn expected_one_of(expected: Vec<String>, found: String, span: Span) -> Self {
Self::new(ErrorKind::ExpectedOneOf { expected, found, span })
}
pub fn expected_closing_tag(expected: String, found: String, span: Span) -> Self {
Self::new(ErrorKind::ExpectedClosingTag { expected, found, span })
}
pub fn invalid_i18n(format: String, span: Span) -> Self {
Self::new(ErrorKind::InvalidI18n { format, span })
}
pub fn unsupported_i18n_format(format: String, span: Span) -> Self {
Self::new(ErrorKind::UnsupportedI18nFormat { format, span })
}
pub fn not_implemented(feature: String, span: Span) -> Self {
Self::new(ErrorKind::NotImplemented { feature, span })
}
pub fn parse_error(message: String, span: Span) -> Self {
Self::new(ErrorKind::Parse { message, span })
}
pub fn parse_float_error(source: String, span: Span) -> Self {
Self::new(ErrorKind::ParseFloatError { source, span })
}
pub fn trailing_content(span: Span) -> Self {
Self::new(ErrorKind::TrailingContent { span })
}
pub fn external_error(source: String, details: String, span: Span) -> Self {
Self::new(ErrorKind::ExternalError { source, details, span })
}
pub fn codegen_error(message: String, span: Span) -> Self {
Self::new(ErrorKind::Codegen { message, span })
}
pub fn regex_error(err: regex::Error) -> Self {
Self::new(ErrorKind::Regex(err))
}
pub fn dialoguer_error(err: dialoguer::Error) -> Self {
Self::new(ErrorKind::Dialoguer(err))
}
pub fn span(&self) -> Span {
self.kind.span()
}
}
impl ErrorKind {
pub fn span(&self) -> Span {
match self {
ErrorKind::Io(_) => Span::unknown(),
ErrorKind::Regex(_) => Span::unknown(),
ErrorKind::Dialoguer(_) => Span::unknown(),
ErrorKind::Parse { span, .. } => *span,
ErrorKind::UnexpectedChar { span, .. } => *span,
ErrorKind::ExpectedChar { span, .. } => *span,
ErrorKind::ExpectedString { span, .. } => *span,
ErrorKind::ExpectedOneOf { span, .. } => *span,
ErrorKind::ExpectedClosingTag { span, .. } => *span,
ErrorKind::InvalidI18n { span, .. } => *span,
ErrorKind::UnsupportedI18nFormat { span, .. } => *span,
ErrorKind::NotImplemented { span, .. } => *span,
ErrorKind::ParseFloatError { span, .. } => *span,
ErrorKind::UnexpectedContent { span, .. } => *span,
ErrorKind::TrailingContent { span, .. } => *span,
ErrorKind::ExternalError { span, .. } => *span,
ErrorKind::Codegen { span, .. } => *span,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &*self.kind {
ErrorKind::Io(err) => write!(f, "IO error: {}", err),
ErrorKind::Regex(err) => write!(f, "Regex error: {}", err),
ErrorKind::Dialoguer(err) => write!(f, "Dialoguer error: {}", err),
ErrorKind::Parse { message, span } => {
write!(f, "Parse error: {}", message)?;
if !span.is_unknown() {
write!(f, " at {}:{}", span.start.line, span.start.column)?;
}
Ok(())
}
ErrorKind::UnexpectedChar { character, span } => {
write!(f, "Unexpected character '{}'", character)?;
if !span.is_unknown() {
write!(f, " at {}:{}", span.start.line, span.start.column)?;
}
Ok(())
}
ErrorKind::ExpectedChar { expected, found, span } => {
write!(f, "Expected '{}', found '{}'", expected, found)?;
if !span.is_unknown() {
write!(f, " at {}:{}", span.start.line, span.start.column)?;
}
Ok(())
}
ErrorKind::ExpectedString { expected, found, span } => {
write!(f, "Expected '{}', found '{}'", expected, found)?;
if !span.is_unknown() {
write!(f, " at {}:{}", span.start.line, span.start.column)?;
}
Ok(())
}
ErrorKind::ExpectedOneOf { expected, found, span } => {
write!(f, "Expected one of {:?}, found '{}'", expected, found)?;
if !span.is_unknown() {
write!(f, " at {}:{}", span.start.line, span.start.column)?;
}
Ok(())
}
ErrorKind::ExpectedClosingTag { expected, found, span } => {
write!(f, "Expected closing tag </{}>, found </{}>", expected, found)?;
if !span.is_unknown() {
write!(f, " at {}:{}", span.start.line, span.start.column)?;
}
Ok(())
}
ErrorKind::InvalidI18n { format, span } => {
write!(f, "Invalid i18n {}", format)?;
if !span.is_unknown() {
write!(f, " at {}:{}", span.start.line, span.start.column)?;
}
Ok(())
}
ErrorKind::UnsupportedI18nFormat { format, span } => {
write!(f, "Unsupported i18n format: {}", format)?;
if !span.is_unknown() {
write!(f, " at {}:{}", span.start.line, span.start.column)?;
}
Ok(())
}
ErrorKind::NotImplemented { feature, span } => {
write!(f, "{} not yet implemented", feature)?;
if !span.is_unknown() {
write!(f, " at {}:{}", span.start.line, span.start.column)?;
}
Ok(())
}
ErrorKind::ParseFloatError { source, span } => {
write!(f, "Failed to parse float: '{}'", source)?;
if !span.is_unknown() {
write!(f, " at {}:{}", span.start.line, span.start.column)?;
}
Ok(())
}
ErrorKind::UnexpectedContent { message, span } => {
write!(f, "Unexpected content: {}", message)?;
if !span.is_unknown() {
write!(f, " at {}:{}", span.start.line, span.start.column)?;
}
Ok(())
}
ErrorKind::TrailingContent { span } => {
write!(f, "Unexpected trailing content")?;
if !span.is_unknown() {
write!(f, " at {}:{}", span.start.line, span.start.column)?;
}
Ok(())
}
ErrorKind::ExternalError { source, details, span } => {
write!(f, "Error in {}: {}", source, details)?;
if !span.is_unknown() {
write!(f, " at {}:{}", span.start.line, span.start.column)?;
}
Ok(())
}
ErrorKind::Codegen { message, span } => {
write!(f, "Codegen error: {}", message)?;
if !span.is_unknown() {
write!(f, " at {}:{}", span.start.line, span.start.column)?;
}
Ok(())
}
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &*self.kind {
ErrorKind::Io(err) => Some(err),
ErrorKind::Regex(err) => Some(err),
ErrorKind::Dialoguer(err) => Some(err),
_ => None,
}
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Error::new(ErrorKind::Io(err))
}
}
impl From<regex::Error> for Error {
fn from(err: regex::Error) -> Self {
Error::new(ErrorKind::Regex(err))
}
}
impl From<dialoguer::Error> for Error {
fn from(err: dialoguer::Error) -> Self {
Error::new(ErrorKind::Dialoguer(err))
}
}
impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Self {
Error::new(ErrorKind::ExternalError { source: "serde_json".to_string(), details: err.to_string(), span: Span::unknown() })
}
}
pub type Result<T> = std::result::Result<T, Error>;