use std::fmt;
use crate::compiler::state::{TypeInfo, TypeState};
use crate::compiler::{
Context, Expression, Span, TypeDef,
expression::{Expr, Resolved},
parser::Node,
value::{Kind, VrlValueConvert},
};
use crate::diagnostic::{DiagnosticMessage, Label, Note, Urls};
pub(crate) type Result = std::result::Result<Not, Error>;
#[derive(Debug, Clone, PartialEq)]
pub struct Not {
inner: Box<Expr>,
}
impl Not {
pub fn new(node: Node<Expr>, not_span: Span, state: &TypeState) -> Result {
let (expr_span, expr) = node.take();
let type_def = expr.type_info(state).result;
if !type_def.is_boolean() {
return Err(Error {
variant: ErrorVariant::NonBoolean(type_def.into()),
not_span,
expr_span,
});
}
Ok(Self {
inner: Box::new(expr),
})
}
}
impl Expression for Not {
fn resolve(&self, ctx: &mut Context) -> Resolved {
Ok((!self.inner.resolve(ctx)?.try_boolean()?).into())
}
fn type_info(&self, state: &TypeState) -> TypeInfo {
let mut state = state.clone();
let result = self.inner.apply_type_info(&mut state);
TypeInfo::new(
state,
TypeDef::boolean()
.maybe_fallible(result.is_fallible())
.with_returns(result.returns().clone()),
)
}
}
impl fmt::Display for Not {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "!{}", self.inner)
}
}
#[derive(Debug)]
pub struct Error {
pub(crate) variant: ErrorVariant,
not_span: Span,
expr_span: Span,
}
#[derive(thiserror::Error, Debug)]
pub(crate) enum ErrorVariant {
#[error("non-boolean negation")]
NonBoolean(Kind),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:#}", self.variant)
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.variant)
}
}
impl DiagnosticMessage for Error {
fn code(&self) -> usize {
use ErrorVariant::NonBoolean;
match &self.variant {
NonBoolean(..) => 660,
}
}
fn labels(&self) -> Vec<Label> {
use ErrorVariant::NonBoolean;
match &self.variant {
NonBoolean(kind) => vec![
Label::primary("negation only works on boolean values", self.not_span),
Label::context(
format!("this expression resolves to {kind}"),
self.expr_span,
),
],
}
}
fn notes(&self) -> Vec<Note> {
use ErrorVariant::NonBoolean;
match &self.variant {
NonBoolean(..) => {
vec![
Note::CoerceValue,
Note::SeeDocs(
"type coercion".to_owned(),
Urls::func_docs("#coerce-functions"),
),
]
}
}
}
}