use std::fmt::Display;
use super::parser::ParseError;
use super::types::{Arity, ColumIndexationBy, DynamicValue};
use crate::dates::ZonedParseError;
fn format_column_indexation_error(
f: &mut std::fmt::Formatter,
indexation: &ColumIndexationBy,
) -> std::fmt::Result {
match indexation {
ColumIndexationBy::Name(name) => write!(f, "cannot find column \"{}\"", name),
ColumIndexationBy::Pos(pos) => write!(f, "column {} out of range", pos),
ColumIndexationBy::NameAndNth(name, nth) => {
write!(f, "cannot find column (\"{}\", {})", name, nth)
}
}
}
#[derive(Debug, PartialEq)]
pub enum ConcretizationError {
ParseError(ParseError),
ColumnNotFound(ColumIndexationBy),
InvalidRegex(String),
UnknownFunction(String),
InvalidArity(String, InvalidArity),
TooManyArguments(usize),
UnknownArgumentName(String),
InvalidCSSSelector(String),
StaticEvaluationError(SpecifiedEvaluationError),
Custom(String),
NotStaticallyAnalyzable,
}
impl Display for ConcretizationError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::ColumnNotFound(indexation) => format_column_indexation_error(f, indexation),
Self::UnknownFunction(name) => write!(f, "unknown function \"{}\"", name),
Self::UnknownArgumentName(arg_name) => write!(f, "unknown argument \"{}\"", arg_name),
Self::ParseError(err) => write!(f, "could not parse expression: {}", err),
Self::InvalidRegex(pattern) => write!(f, "invalid regex {}", pattern),
Self::InvalidArity(name, arity) => write!(f, "{}: {}", name, arity),
Self::TooManyArguments(actual) => {
write!(f, "got {} arguments. Cannot exceed 8.", actual)
}
Self::InvalidCSSSelector(css) => write!(f, "invalid css selector: {}", css),
Self::StaticEvaluationError(error) => error.fmt(f),
Self::Custom(msg) => write!(f, "{}", msg),
Self::NotStaticallyAnalyzable => write!(f, "not statically analyzable"),
}
}
}
#[derive(Debug, PartialEq)]
pub struct InvalidArity {
expected: Arity,
got: usize,
}
impl InvalidArity {
pub fn from_arity(arity: Arity, got: usize) -> Self {
Self {
expected: arity,
got,
}
}
}
impl Display for InvalidArity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.expected {
Arity::Min(min) => write!(
f,
"expected at least {} argument{} but got {}",
min,
if min > &1 { "s" } else { "" },
self.got
),
Arity::Strict(arity) => write!(
f,
"expected {} argument{} but got {}",
arity,
if arity > &1 { "s" } else { "" },
self.got
),
Arity::Range(range) => write!(
f,
"expected between {} and {} arguments but got {}",
range.start(),
range.end(),
self.got
),
}
}
}
#[derive(Debug, PartialEq)]
pub struct SpecifiedEvaluationError {
pub function_name: String,
pub reason: EvaluationError,
}
impl SpecifiedEvaluationError {
pub fn new(name: &str, reason: EvaluationError) -> Self {
Self {
function_name: name.to_string(),
reason,
}
}
}
impl Display for SpecifiedEvaluationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{} {}",
if self.function_name.starts_with('<') || self.function_name.starts_with('>') {
self.function_name.clone()
} else {
format!("{}()", self.function_name)
},
self.reason
)
}
}
#[derive(Debug, PartialEq)]
pub enum EvaluationError {
InvalidArity(InvalidArity),
InvalidPath,
InvalidLambda,
NotImplemented(String),
IO(String),
DateTime(String),
Cast {
from_value: DynamicValue,
to_type: String,
},
Custom(String),
UnsupportedEncoding(String),
UnsupportedDecoderTrap(String),
DecodeError,
ColumnNotFound(ColumIndexationBy),
ColumnOutOfRange(usize),
GlobalVariableOutOfRange(usize),
UnicodeDecodeError,
JSONParseError(String),
UnfillableUnderscore,
PluralClauseMisalignment {
got: usize,
names: Vec<String>,
},
}
impl EvaluationError {
pub fn from_cast(from_value: &DynamicValue, expected: &str) -> Self {
Self::Cast {
from_value: from_value.clone(),
to_type: expected.to_string(),
}
}
pub fn from_zoned_parse_error(
value: &str,
format: Option<&str>,
timezone: Option<&str>,
error: ZonedParseError,
) -> Self {
Self::DateTime(match error {
ZonedParseError::CannotParse => format!(
"cannot parse \"{}\" as a datetime, consider using datetime() with a custom format",
value
),
ZonedParseError::TimezoneMismatch => format!(
"conflicting timezones between \"{}\" and \"{}\"",
value,
timezone.unwrap()
),
ZonedParseError::InvalidFormat => {
format!("invalid strptime format: \"{}\"", format.unwrap())
}
})
}
pub fn specify(self, function_name: &str) -> SpecifiedEvaluationError {
SpecifiedEvaluationError {
function_name: function_name.to_string(),
reason: self,
}
}
pub fn anonymous(self) -> SpecifiedEvaluationError {
SpecifiedEvaluationError {
function_name: "<expr>".to_string(),
reason: self,
}
}
pub fn plural_clause_misalignment(names: &[String], got: usize) -> Self {
Self::PluralClauseMisalignment {
got,
names: names.to_owned(),
}
}
}
impl Display for EvaluationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidPath => write!(f, "invalid posix path"),
Self::InvalidArity(arity) => arity.fmt(f),
Self::InvalidLambda => write!(f, "provided argument is not a lambda"),
Self::IO(msg) => write!(f, "{}", msg),
Self::DateTime(msg) => write!(f, "{}", msg),
Self::Custom(msg) => write!(f, "{}", msg),
Self::Cast {
from_value,
to_type,
} => write!(
f,
"cannot safely cast {:?} from type \"{}\" to type \"{}\"",
from_value,
from_value.type_of(),
to_type
),
Self::NotImplemented(t) => {
write!(f, "not implemented for values of type \"{}\" as of yet", t)
}
Self::UnsupportedEncoding(name) => write!(f, "unsupported encoding \"{}\"", name),
Self::UnsupportedDecoderTrap(name) => {
write!(
f,
"unsupported encoder trap \"{}\". Must be one of strict, replace, ignore.",
name
)
}
Self::DecodeError => write!(f, "could not decode"),
Self::ColumnNotFound(indexation) => format_column_indexation_error(f, indexation),
Self::ColumnOutOfRange(idx) => write!(f, "column {} is out of range", idx),
Self::GlobalVariableOutOfRange(idx) => {
write!(f, "global variable index={} is out of range", idx)
}
Self::UnicodeDecodeError => write!(f, "unicode decode error"),
Self::JSONParseError(msg) => write!(f, "cannot parse {} as json", msg),
Self::UnfillableUnderscore => write!(
f,
"some underscore `_` was not fillable because it is not downstream of a pipe"
),
Self::PluralClauseMisalignment { got, names } => write!(
f,
"plural clause related to columns ({}) yielded {} items instead of {}",
names.join(", "),
got,
names.len()
),
}
}
}
#[cfg(test)]
#[derive(Debug, PartialEq)]
pub enum RunError {
Prepare(ConcretizationError),
Evaluation(SpecifiedEvaluationError),
}