use crate::{
operator::Operator, token::PartialToken, value::value_type::ValueType, value::Value, Node,
ToolInfo,
};
use std::{fmt, io, time::SystemTimeError};
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum Error {
TypeCheckFailure {
tool_info: ToolInfo<'static>,
argument: Value,
},
AssertEqualFailed {
expected: Value,
actual: Value,
},
AssertFailed,
WrongColumnAmount {
expected: usize,
actual: usize,
},
ExpectedOperatorArgumentAmount {
expected: usize,
actual: usize,
},
ExpectedFunctionArgumentAmount {
identifier: String,
expected: usize,
actual: usize,
},
ExpectedAtLeastFunctionArgumentAmount {
identifier: String,
minimum: usize,
actual: usize,
},
ExpectedString {
actual: Value,
},
ExpectedInt {
actual: Value,
},
ExpectedFloat {
actual: Value,
},
ExpectedNumber {
actual: Value,
},
ExpectedNumberOrString {
actual: Value,
},
ExpectedBoolean {
actual: Value,
},
ExpectedList {
actual: Value,
},
ExpectedFixedLenList {
expected_len: usize,
actual: Value,
},
ExpectedEmpty {
actual: Value,
},
ExpectedMap {
actual: Value,
},
ExpectedTable {
actual: Value,
},
ExpectedFunction {
actual: Value,
},
ExpectedCollection {
actual: Value,
},
AppendedToLeafNode(Node),
PrecedenceViolation,
VariableIdentifierNotFound(String),
FunctionIdentifierNotFound(String),
TypeError {
expected: &'static [ValueType],
actual: Value,
},
MacroArgumentType {
macro_info: ToolInfo<'static>,
actual: Value,
},
WrongTypeCombination {
operator: Operator,
actual: Vec<ValueType>,
},
UnmatchedLBrace,
UnmatchedRBrace,
MissingOperatorOutsideOfBrace,
UnmatchedPartialToken {
first: PartialToken,
second: Option<PartialToken>,
},
AdditionError {
augend: Value,
addend: Value,
},
SubtractionError {
minuend: Value,
subtrahend: Value,
},
NegationError {
argument: Value,
},
MultiplicationError {
multiplicand: Value,
multiplier: Value,
},
DivisionError {
dividend: Value,
divisor: Value,
},
ModulationError {
dividend: Value,
divisor: Value,
},
InvalidRegex {
regex: String,
message: String,
},
ContextNotMutable,
IllegalEscapeSequence(String),
BuiltinFunctionsCannotBeEnabled,
BuiltinFunctionsCannotBeDisabled,
MacroFailure(String),
CustomMessage(String),
}
impl From<csv::Error> for Error {
fn from(value: csv::Error) -> Self {
Error::MacroFailure(value.to_string())
}
}
impl From<json::Error> for Error {
fn from(value: json::Error) -> Self {
Error::MacroFailure(value.to_string())
}
}
impl From<io::Error> for Error {
fn from(value: std::io::Error) -> Self {
Error::MacroFailure(value.to_string())
}
}
impl From<git2::Error> for Error {
fn from(value: git2::Error) -> Self {
Error::MacroFailure(value.to_string())
}
}
impl From<reqwest::Error> for Error {
fn from(value: reqwest::Error) -> Self {
Error::MacroFailure(value.to_string())
}
}
impl From<serde_json::Error> for Error {
fn from(value: serde_json::Error) -> Self {
Error::MacroFailure(value.to_string())
}
}
impl From<SystemTimeError> for Error {
fn from(value: SystemTimeError) -> Self {
Error::MacroFailure(value.to_string())
}
}
impl From<trash::Error> for Error {
fn from(value: trash::Error) -> Self {
Error::MacroFailure(value.to_string())
}
}
impl From<toml::de::Error> for Error {
fn from(value: toml::de::Error) -> Self {
Error::MacroFailure(value.to_string())
}
}
impl Error {
pub(crate) fn expect_operator_argument_amount(actual: usize, expected: usize) -> Result<()> {
if actual == expected {
Ok(())
} else {
Err(Error::ExpectedOperatorArgumentAmount { expected, actual })
}
}
pub(crate) fn expect_function_argument_amount(
identifier: &str,
actual: usize,
expected: usize,
) -> Result<()> {
if actual == expected {
Ok(())
} else {
Err(Error::ExpectedFunctionArgumentAmount {
identifier: identifier.to_string(),
expected,
actual,
})
}
}
pub(crate) fn expected_minimum_function_argument_amount(
identifier: &str,
actual: usize,
minimum: usize,
) -> Result<()> {
if actual >= minimum {
Ok(())
} else {
Err(Error::ExpectedAtLeastFunctionArgumentAmount {
identifier: identifier.to_string(),
minimum,
actual,
})
}
}
pub fn type_error(actual: Value, expected: &'static [ValueType]) -> Self {
Error::TypeError { actual, expected }
}
pub fn wrong_type_combination(operator: Operator, actual: Vec<ValueType>) -> Self {
Error::WrongTypeCombination { operator, actual }
}
pub fn expected_string(actual: Value) -> Self {
Error::ExpectedString { actual }
}
pub fn expected_int(actual: Value) -> Self {
Error::ExpectedInt { actual }
}
pub fn expected_float(actual: Value) -> Self {
Error::ExpectedFloat { actual }
}
pub fn expected_number(actual: Value) -> Self {
Error::ExpectedNumber { actual }
}
pub fn expected_number_or_string(actual: Value) -> Self {
Error::ExpectedNumberOrString { actual }
}
pub fn expected_boolean(actual: Value) -> Self {
Error::ExpectedBoolean { actual }
}
pub fn expected_list(actual: Value) -> Self {
Error::ExpectedList { actual }
}
pub fn expected_fixed_len_list(expected_len: usize, actual: Value) -> Self {
Error::ExpectedFixedLenList {
expected_len,
actual,
}
}
pub fn expected_empty(actual: Value) -> Self {
Error::ExpectedEmpty { actual }
}
pub fn expected_map(actual: Value) -> Self {
Error::ExpectedMap { actual }
}
pub fn expected_table(actual: Value) -> Self {
Error::ExpectedTable { actual }
}
pub fn expected_function(actual: Value) -> Self {
Error::ExpectedFunction { actual }
}
pub fn expected_collection(actual: Value) -> Self {
Error::ExpectedCollection { actual }
}
pub(crate) fn unmatched_partial_token(
first: PartialToken,
second: Option<PartialToken>,
) -> Self {
Error::UnmatchedPartialToken { first, second }
}
pub(crate) fn addition_error(augend: Value, addend: Value) -> Self {
Error::AdditionError { augend, addend }
}
pub(crate) fn subtraction_error(minuend: Value, subtrahend: Value) -> Self {
Error::SubtractionError {
minuend,
subtrahend,
}
}
pub(crate) fn negation_error(argument: Value) -> Self {
Error::NegationError { argument }
}
pub(crate) fn multiplication_error(multiplicand: Value, multiplier: Value) -> Self {
Error::MultiplicationError {
multiplicand,
multiplier,
}
}
pub(crate) fn division_error(dividend: Value, divisor: Value) -> Self {
Error::DivisionError { dividend, divisor }
}
pub(crate) fn modulation_error(dividend: Value, divisor: Value) -> Self {
Error::ModulationError { dividend, divisor }
}
pub fn invalid_regex(regex: String, message: String) -> Self {
Error::InvalidRegex { regex, message }
}
}
pub fn expect_number_or_string(actual: &Value) -> Result<()> {
match actual {
Value::String(_) | Value::Float(_) | Value::Integer(_) => Ok(()),
_ => Err(Error::expected_number_or_string(actual.clone())),
}
}
pub fn _expect_collection(actual: &Value) -> Result<()> {
match actual {
Value::String(_) | Value::List(_) | Value::Map(_) | Value::Table(_) => Ok(()),
_ => Err(Error::expected_collection(actual.clone())),
}
}
impl std::error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;
match self {
TypeCheckFailure { tool_info, argument } => write!(f, "Type check failure. This is a bug with the tool or with Dust's internal type checking. Please report this bug and include this error message.\nToolInfo = {tool_info:?}\nargument = {argument}"),
AssertEqualFailed {expected, actual } => write!(f, "Equality assertion failed. {expected} does not equal {actual}."),
AssertFailed => write!(f, "Assertion failed. A false value was passed to \"assert\"."),
ExpectedOperatorArgumentAmount { expected, actual } => write!(
f,
"An operator expected {} arguments, but got {}.",
expected, actual
),
ExpectedFunctionArgumentAmount {
expected,
actual,
identifier,
} => write!(
f,
"{identifier} expected {expected} arguments, but got {actual}.",
),
ExpectedAtLeastFunctionArgumentAmount {
minimum,
actual,
identifier,
} => write!(
f,
"{identifier} expected a minimum of {minimum} arguments, but got {actual}.",
),
ExpectedString { actual } => {
write!(f, "Expected a Value::String, but got {:?}.", actual)
}
ExpectedInt { actual } => write!(f, "Expected a Value::Int, but got {:?}.", actual),
ExpectedFloat { actual } => write!(f, "Expected a Value::Float, but got {:?}.", actual),
ExpectedNumber { actual } => write!(
f,
"Expected a Value::Float or Value::Int, but got {:?}.",
actual
),
ExpectedNumberOrString { actual } => write!(
f,
"Expected a Value::Number or a Value::String, but got {:?}.",
actual
),
ExpectedBoolean { actual } => {
write!(f, "Expected a Value::Boolean, but got {:?}.", actual)
}
ExpectedList { actual } => write!(f, "Expected a Value::Tuple, but got {:?}.", actual),
ExpectedFixedLenList {
expected_len,
actual,
} => write!(
f,
"Expected a Value::Tuple of len {}, but got {:?}.",
expected_len, actual
),
ExpectedEmpty { actual } => write!(f, "Expected a Value::Empty, but got {:?}.", actual),
ExpectedMap { actual } => write!(f, "Expected a Value::Map, but got {:?}.", actual),
ExpectedTable { actual } => write!(f, "Expected a Value::Table, but got {:?}.", actual),
ExpectedFunction { actual } => {
write!(f, "Expected Value::Function, but got {:?}.", actual)
}
ExpectedCollection { actual } => {
write!(
f,
"Expected a string, list, map or table, but got {:?}.",
actual
)
}
AppendedToLeafNode(node) => write!(f, "Syntax error at \"{node}\"."),
PrecedenceViolation => write!(
f,
"Tried to append a node to another node with higher precedence."
),
VariableIdentifierNotFound(identifier) => write!(
f,
"Variable identifier is not bound to anything by context: {:?}.",
identifier
),
FunctionIdentifierNotFound(identifier) => write!(
f,
"Function identifier is not bound to anything by context: {:?}.",
identifier
),
TypeError { expected, actual } => {
write!(
f,
"Type Error. The value {actual} is not one of the following: {expected:?}.",
)
}
WrongTypeCombination { operator, actual } => write!(
f,
"The operator {:?} was called with a wrong combination of types: {:?}",
operator, actual
),
UnmatchedLBrace => write!(f, "Found an unmatched opening parenthesis '('."),
UnmatchedRBrace => write!(f, "Found an unmatched closing parenthesis ')'."),
MissingOperatorOutsideOfBrace { .. } => write!(
f,
"Found an opening parenthesis that is preceded by something that does not take \
any arguments on the right, or found a closing parenthesis that is succeeded by \
something that does not take any arguments on the left."
),
UnmatchedPartialToken { first, second } => {
if let Some(second) = second {
write!(
f,
"Found a partial token '{}' that should not be followed by '{}'.",
first, second
)
} else {
write!(
f,
"Found a partial token '{}' that should be followed by another partial \
token.",
first
)
}
}
AdditionError { augend, addend } => write!(f, "Error adding {} + {}", augend, addend),
SubtractionError {
minuend,
subtrahend,
} => write!(f, "Error subtracting {} - {}", minuend, subtrahend),
NegationError { argument } => write!(f, "Error negating -{}", argument),
MultiplicationError {
multiplicand,
multiplier,
} => write!(f, "Error multiplying {} * {}", multiplicand, multiplier),
DivisionError { dividend, divisor } => {
write!(f, "Error dividing {} / {}", dividend, divisor)
}
ModulationError { dividend, divisor } => {
write!(f, "Error modulating {} % {}", dividend, divisor)
}
InvalidRegex { regex, message } => write!(
f,
"Regular expression {:?} is invalid: {:?}",
regex, message
),
ContextNotMutable => write!(f, "Cannot manipulate context"),
BuiltinFunctionsCannotBeEnabled => {
write!(f, "This context does not allow enabling builtin functions")
}
BuiltinFunctionsCannotBeDisabled => {
write!(f, "This context does not allow disabling builtin functions")
}
IllegalEscapeSequence(string) => write!(f, "Illegal escape sequence: {}", string),
MacroFailure(message) => write!(f, "Function failure: {}", message),
CustomMessage(message) => write!(f, "Error: {}", message),
WrongColumnAmount { expected, actual } => write!(
f,
"Wrong number of columns for this table. Expected {expected}, found {actual}."
),
MacroArgumentType {
macro_info,
actual,
} => write!(
f,
"Wrong argument of type {:?} was passed to {}. Expected one of the following types: {:?}.",
actual.value_type(),
macro_info.identifier,
macro_info.inputs
),
}
}
}