use core::fmt;
use crate::dim::Dim;
use crate::kind::Kind;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
Parse {
input: String,
position: usize,
message: String,
},
InvalidNumber {
input: String,
},
UnknownUnit {
symbol: String,
suggestions: Vec<String>,
},
AmbiguousSymbol {
symbol: String,
candidates: Vec<String>,
},
AmbiguousExpression {
input: String,
message: String,
},
DimensionMismatch {
from: Dim,
to: Dim,
},
KindMismatch {
from: Kind,
to: Kind,
operation: &'static str,
suggestions: Vec<String>,
},
Overflow {
operation: &'static str,
},
DivisionByZero,
LengthMismatch {
input: usize,
output: usize,
},
Refused {
reason: String,
suggestions: Vec<String>,
},
InvalidDefinition {
symbol: String,
message: String,
},
DuplicateSymbol {
symbol: String,
},
}
impl Error {
pub const ALL_CODES: &'static [&'static str] = &[
"parse_error",
"invalid_number",
"unknown_unit",
"ambiguous_symbol",
"ambiguous_expression",
"dimension_mismatch",
"kind_mismatch",
"overflow",
"division_by_zero",
"length_mismatch",
"refused",
"invalid_definition",
"duplicate_symbol",
];
#[must_use]
pub const fn code(&self) -> &'static str {
match self {
Self::Parse { .. } => "parse_error",
Self::InvalidNumber { .. } => "invalid_number",
Self::UnknownUnit { .. } => "unknown_unit",
Self::AmbiguousSymbol { .. } => "ambiguous_symbol",
Self::AmbiguousExpression { .. } => "ambiguous_expression",
Self::DimensionMismatch { .. } => "dimension_mismatch",
Self::KindMismatch { .. } => "kind_mismatch",
Self::Overflow { .. } => "overflow",
Self::DivisionByZero => "division_by_zero",
Self::LengthMismatch { .. } => "length_mismatch",
Self::Refused { .. } => "refused",
Self::InvalidDefinition { .. } => "invalid_definition",
Self::DuplicateSymbol { .. } => "duplicate_symbol",
}
}
#[must_use]
pub fn suggestions(&self) -> &[String] {
match self {
Self::AmbiguousSymbol { candidates, .. } => candidates,
Self::UnknownUnit { suggestions, .. }
| Self::KindMismatch { suggestions, .. }
| Self::Refused { suggestions, .. } => suggestions,
_ => &[],
}
}
#[must_use]
pub fn candidates(&self) -> &[String] {
match self {
Self::AmbiguousSymbol { candidates, .. } => candidates,
Self::UnknownUnit { suggestions, .. } => suggestions,
_ => &[],
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Parse {
input,
position,
message,
} => {
write!(
f,
"cannot parse '{input}' at position {position}: {message}"
)
}
Self::InvalidNumber { input } => write!(f, "'{input}' is not a valid number"),
Self::UnknownUnit {
symbol,
suggestions,
} => {
write!(f, "unknown unit '{symbol}'")?;
if !suggestions.is_empty() {
write!(f, " (did you mean: {}?)", suggestions.join(", "))?;
}
Ok(())
}
Self::AmbiguousSymbol { symbol, candidates } => write!(
f,
"'{symbol}' is ambiguous: use {}, or load a config that resolves it",
candidates.join(" or ")
),
Self::AmbiguousExpression { input, message } => {
write!(f, "ambiguous expression '{input}': {message}")
}
Self::DimensionMismatch { from, to } => {
write!(
f,
"dimension mismatch: [{from}] cannot be converted to [{to}]"
)
}
Self::KindMismatch {
from,
to,
operation,
suggestions,
} => {
write!(
f,
"kind mismatch in {operation}: {from} and {to} are not compatible"
)?;
if !suggestions.is_empty() {
write!(
f,
" (a temperature point and a temperature difference are different kinds; \
for a difference use {})",
suggestions.join(" or ")
)?;
}
Ok(())
}
Self::Overflow { operation } => {
write!(f, "integer overflow in exact arithmetic ({operation})")
}
Self::DivisionByZero => f.write_str("division by zero"),
Self::LengthMismatch { input, output } => {
write!(
f,
"input has {input} values but output has room for {output}"
)
}
Self::Refused { reason, .. } => write!(f, "refused: {reason}"),
Self::InvalidDefinition { symbol, message } => {
write!(f, "invalid definition of '{symbol}': {message}")
}
Self::DuplicateSymbol { symbol } => write!(f, "symbol '{symbol}' is already defined"),
}
}
}
impl std::error::Error for Error {}
pub type Result<T> = core::result::Result<T, Error>;