use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub enum ParseErrorKind {
Other(String),
UnknownExpression(String),
WrongArgCount {
op: String,
expected: String,
found: usize,
},
TypeMismatch { expected: String, found: String },
NotComparable { op: String, ty: String },
CannotCompare { lhs: String, rhs: String },
NotInterpolatable(String),
UnboundVariable(String),
Zoom(&'static str),
RequiresExactlyOneArg { op: String, found: usize },
ExpectedOneArgument,
ExpectedArgsOfType { sig: String, found: String },
MatchAtLeast4 { found: usize },
ArrayItemType,
ArrayLength,
BareObject,
EmptyArray,
ExpressionNameNotString { found: &'static str },
GlobalStateProperty { found: String },
AscendingStops { kind: String },
ExponentialBase,
CubicBezier,
CollatorOptions,
NumberFormatExclusive,
SliceFirstArg { found: String },
SearchNeedle { found: String },
BranchLabelsType,
BranchLabelsUnique,
BranchLabelsEmpty,
BranchLabelNotInteger,
BranchLabelTooLarge,
GeojsonPolygon { op: String },
LetBindingNameString,
VarBindingName,
NumberFormatOptionsObject,
VerticalAlign { found: String },
ExpectedEvenArgs { op: &'static str },
ExpectedOddArgsCase,
ExpectedOddArgsLet,
FormatAtLeastOne,
CollatorOneArg,
NumberFormatTwoArgs,
ExpectedNArgs { n: usize, found: usize },
FormatFirstSection,
ExtArgCount {
kind: &'static str,
op: String,
expected: usize,
found: usize,
},
MacroDepth { op: String },
InterpolationStopNumber,
StepStopNumber,
InterpolationTypeArray,
InterpolationTypeName,
UnknownInterpolationType { name: String },
CollatorNonString,
ExpectedStringOrArray { found: String },
FormattedTextType,
VariableName,
}
impl fmt::Display for ParseErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ParseErrorKind::Other(s) => write!(f, "{s}"),
ParseErrorKind::UnknownExpression(op) => write!(
f,
"Unknown expression \"{op}\". If you wanted a literal array, use [\"literal\", [...]]."
),
ParseErrorKind::WrongArgCount {
op,
expected,
found,
} => {
let _ = op;
write!(f, "Expected {expected}, but found {found} instead.")
}
ParseErrorKind::TypeMismatch { expected, found } => {
write!(f, "Expected {expected} but found {found} instead.")
}
ParseErrorKind::NotComparable { op, ty } => {
write!(f, "\"{op}\" comparisons are not supported for type '{ty}'.")
}
ParseErrorKind::CannotCompare { lhs, rhs } => {
write!(f, "Cannot compare types '{lhs}' and '{rhs}'.")
}
ParseErrorKind::NotInterpolatable(ty) => write!(f, "Type {ty} is not interpolatable."),
ParseErrorKind::UnboundVariable(name) => write!(
f,
"Unknown variable \"{name}\". Make sure \"{name}\" has been bound in an enclosing \"let\" expression before using it."
),
ParseErrorKind::Zoom(msg) => write!(f, "{msg}"),
ParseErrorKind::RequiresExactlyOneArg { op, found } => write!(
f,
"'{op}' expression requires exactly one argument, but found {found} instead."
),
ParseErrorKind::ExpectedOneArgument => write!(f, "Expected one argument."),
ParseErrorKind::ExpectedArgsOfType { sig, found } => write!(
f,
"Expected arguments of type {sig}, but found ({found}) instead."
),
ParseErrorKind::MatchAtLeast4 { found } => {
write!(f, "Expected at least 4 arguments, but found only {found}.")
}
ParseErrorKind::ArrayItemType => write!(
f,
"The item type argument of \"array\" must be one of string, number, boolean"
),
ParseErrorKind::ArrayLength => write!(
f,
"The length argument to \"array\" must be a positive integer literal"
),
ParseErrorKind::BareObject => {
write!(f, "Bare objects invalid. Use [\"literal\", {{...}}] instead.")
}
ParseErrorKind::EmptyArray => write!(
f,
"Expected an array with at least one element. If you wanted a literal array, use [\"literal\", []]."
),
ParseErrorKind::ExpressionNameNotString { found } => write!(
f,
"Expression name must be a string, but found {found} instead. If you wanted a literal array, use [\"literal\", [...]]."
),
ParseErrorKind::GlobalStateProperty { found } => {
write!(f, "Global state property must be string, but found {found} instead.")
}
ParseErrorKind::AscendingStops { kind } => write!(
f,
"Input/output pairs for \"{kind}\" expressions must be arranged with input values in strictly ascending order."
),
ParseErrorKind::ExponentialBase => {
write!(f, "Exponential interpolation requires a numeric base.")
}
ParseErrorKind::CubicBezier => write!(
f,
"Cubic bezier interpolation requires four numeric arguments with values between 0 and 1."
),
ParseErrorKind::CollatorOptions => {
write!(f, "Collator options argument must be an object.")
}
ParseErrorKind::NumberFormatExclusive => write!(
f,
"NumberFormat options `currency` and `unit` are mutually exclusive"
),
ParseErrorKind::SliceFirstArg { found } => write!(
f,
"Expected first argument to be of type array or string, but found {found} instead"
),
ParseErrorKind::SearchNeedle { found } => write!(
f,
"Expected first argument to be of type boolean, string, number or null, but found {found} instead"
),
ParseErrorKind::BranchLabelsType => {
write!(f, "Branch labels must be numbers or strings.")
}
ParseErrorKind::BranchLabelsUnique => write!(f, "Branch labels must be unique."),
ParseErrorKind::BranchLabelsEmpty => write!(f, "Expected at least one branch label."),
ParseErrorKind::BranchLabelNotInteger => {
write!(f, "Numeric branch labels must be integer values.")
}
ParseErrorKind::BranchLabelTooLarge => write!(
f,
"Branch labels must be integers no larger than 9007199254740991."
),
ParseErrorKind::GeojsonPolygon { op } => write!(
f,
"'{op}' expression requires valid geojson object that contains polygon geometry type."
),
ParseErrorKind::LetBindingNameString => {
write!(f, "'let' binding names must be strings.")
}
ParseErrorKind::VarBindingName => write!(f, "'var' requires a string binding name."),
ParseErrorKind::NumberFormatOptionsObject => {
write!(f, "'number-format' options must be an object.")
}
ParseErrorKind::VerticalAlign { found } => write!(
f,
"'vertical-align' must be one of: 'bottom', 'center', 'top' but found '{found}' instead."
),
ParseErrorKind::ExpectedEvenArgs { op } => {
write!(f, "Expected an even number of arguments (>= 4) to '{op}'.")
}
ParseErrorKind::ExpectedOddArgsCase => {
write!(f, "Expected an odd number of arguments (>= 3) to 'case'.")
}
ParseErrorKind::ExpectedOddArgsLet => {
write!(f, "Expected an odd number of arguments to 'let'.")
}
ParseErrorKind::FormatAtLeastOne => {
write!(f, "Expected at least one argument to 'format'.")
}
ParseErrorKind::CollatorOneArg => write!(f, "Expected one argument to 'collator'."),
ParseErrorKind::NumberFormatTwoArgs => {
write!(f, "Expected two arguments to 'number-format'.")
}
ParseErrorKind::ExpectedNArgs { n, found } => {
write!(f, "Expected {n} arguments, but found {found} instead.")
}
ParseErrorKind::FormatFirstSection => {
write!(f, "First argument to 'format' must be an image or text section.")
}
ParseErrorKind::ExtArgCount {
kind,
op,
expected,
found,
} => write!(f, "{kind} '{op}' expects {expected} argument(s), found {found}."),
ParseErrorKind::MacroDepth { op } => {
write!(f, "Macro expansion too deep expanding '{op}' (recursive macro?).")
}
ParseErrorKind::InterpolationStopNumber => {
write!(f, "Interpolation stop inputs must be numbers.")
}
ParseErrorKind::StepStopNumber => write!(f, "Step stop inputs must be numbers."),
ParseErrorKind::InterpolationTypeArray => {
write!(f, "Interpolation type must be an array, e.g. [\"linear\"].")
}
ParseErrorKind::InterpolationTypeName => {
write!(f, "Interpolation type name must be a string.")
}
ParseErrorKind::UnknownInterpolationType { name } => {
write!(f, "Unknown interpolation type \"{name}\".")
}
ParseErrorKind::CollatorNonString => {
write!(f, "Cannot use collator to compare non-string types.")
}
ParseErrorKind::ExpectedStringOrArray { found } => write!(
f,
"Expected argument of type string or array, but found {found} instead."
),
ParseErrorKind::FormattedTextType => write!(
f,
"Formatted text type must be 'string', 'value', 'image' or 'null'."
),
ParseErrorKind::VariableName => write!(
f,
"Variable names must contain only alphanumeric characters or '_'."
),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ParseError {
pub kind: ParseErrorKind,
pub key: String,
}
impl ParseError {
pub fn of(kind: ParseErrorKind) -> ParseError {
ParseError {
kind,
key: String::new(),
}
}
pub fn new(message: impl Into<String>) -> ParseError {
ParseError::of(ParseErrorKind::Other(message.into()))
}
pub(crate) fn at(mut self, index: usize) -> ParseError {
self.key = format!("[{index}]{}", self.key);
self
}
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.kind)
}
}
impl std::error::Error for ParseError {}
#[derive(Debug, Clone, PartialEq)]
pub enum EvalErrorKind {
Other(String),
TypeMismatch { expected: String, found: String },
TypeMismatchArg {
arg: &'static str,
expected: String,
found: String,
},
CouldNotParse { ty: &'static str, value: String },
CouldNotConvertToNumber { value: String },
ArrayIndexNegative { index: f64 },
ArrayIndexOutOfBounds { index: f64, max: usize },
ArrayIndexNotInteger { index: f64 },
InvalidRgba { value: String, reason: &'static str },
NotOrderedComparable {
op: String,
lhs: String,
rhs: String,
},
SearchNeedle { found: String },
InterpolationOutputs,
MaxCallDepth { op: String },
ZoomUnavailable,
Unimplemented { op: String },
UnknownVariable { name: String },
}
impl fmt::Display for EvalErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EvalErrorKind::Other(s) => write!(f, "{s}"),
EvalErrorKind::TypeMismatch { expected, found } => write!(
f,
"Expected value to be of type {expected}, but found {found} instead."
),
EvalErrorKind::TypeMismatchArg {
arg,
expected,
found,
} => write!(
f,
"Expected {arg} to be of type {expected}, but found {found} instead."
),
EvalErrorKind::CouldNotParse { ty, value } => {
write!(f, "Could not parse {ty} from value '{value}'")
}
EvalErrorKind::CouldNotConvertToNumber { value } => {
write!(f, "Could not convert {value} to number.")
}
EvalErrorKind::ArrayIndexNegative { index } => {
write!(f, "Array index out of bounds: {index} < 0.")
}
EvalErrorKind::ArrayIndexOutOfBounds { index, max } => {
write!(f, "Array index out of bounds: {index} > {max}.")
}
EvalErrorKind::ArrayIndexNotInteger { index } => {
write!(f, "Array index must be an integer, but found {index} instead.")
}
EvalErrorKind::InvalidRgba { value, reason } => {
write!(f, "Invalid rgba value {value}: {reason}")
}
EvalErrorKind::NotOrderedComparable { op, lhs, rhs } => write!(
f,
"Expected arguments for \"{op}\" to be (string, string) or (number, number), but found ({lhs}, {rhs}) instead."
),
EvalErrorKind::SearchNeedle { found } => write!(
f,
"Expected first argument to be of type boolean, string, number or null, but found {found} instead."
),
EvalErrorKind::InterpolationOutputs => write!(
f,
"Interpolation outputs must be numbers, colors, or arrays of numbers."
),
EvalErrorKind::MaxCallDepth { op } => {
write!(f, "Maximum call depth exceeded calling function '{op}'.")
}
EvalErrorKind::ZoomUnavailable => {
write!(f, "The 'zoom' expression is unavailable here.")
}
EvalErrorKind::Unimplemented { op } => write!(f, "Unimplemented operator \"{op}\"."),
EvalErrorKind::UnknownVariable { name } => write!(f, "Unknown variable \"{name}\"."),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct EvalError {
pub kind: EvalErrorKind,
}
impl EvalError {
pub fn of(kind: EvalErrorKind) -> EvalError {
EvalError { kind }
}
pub fn new(message: impl Into<String>) -> EvalError {
EvalError::of(EvalErrorKind::Other(message.into()))
}
}
impl fmt::Display for EvalError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.kind)
}
}
impl std::error::Error for EvalError {}