use std::fmt;
use miette::{Diagnostic, SourceSpan};
use thiserror::Error;
use super::ast::{EdgeOperator, Span};
use super::{D2Import, D2Parse};
use crate::diagnostic::{SourceError, SourceLanguage};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct D2Language;
pub type SyntaxError = SourceError<D2Language>;
#[derive(Debug, Clone, PartialEq, Eq, Error, Diagnostic)]
#[error("{construct} is outside svugy's d2 subset and is ignored")]
#[diagnostic(code(svugy::d2::unsupported), severity(Warning))]
pub struct UnsupportedConstruct {
pub construct: Construct,
#[label("ignored")]
pub span: SourceSpan,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Construct {
Variables,
Classes,
Board(&'static str),
Import,
Glob,
EdgeReference,
SpecialShape(String),
BlockString,
Null,
Array,
ArrayMap,
ReservedField(&'static str),
FieldForm(String),
DeclarationInEdgeMap,
UnmappedShape(String),
UnmappedDirection(String),
ArrowheadForm(EdgeOperator),
FieldOnEdge(&'static str),
FieldOnRoot(&'static str),
UnknownStyleKey(String),
ContainerBadge,
}
pub(crate) type Diagnostics = crate::diagnostic::Diagnostics<D2Language>;
impl SourceLanguage for D2Language {
const ERROR_CODE: &'static str = "svugy::d2::syntax";
type Warning = UnsupportedConstruct;
fn import(source_text: &str) -> D2Import {
let parsed = D2Parse::parse(source_text);
parsed.lower()
}
}
impl Diagnostics {
pub(crate) fn record_warning(&mut self, construct: Construct, span: Span) {
let span = SourceSpan::from(span);
let warning = UnsupportedConstruct { construct, span };
self.warnings.push(warning);
}
}
impl fmt::Display for Construct {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Variables => write!(formatter, "a variable (`vars`, `${{…}}`)"),
Self::Classes => write!(formatter, "a class (`classes`, `class`)"),
Self::Board(keyword) => write!(formatter, "a board (`{keyword}`)"),
Self::Import => write!(formatter, "an import (`@…`)"),
Self::Glob => write!(formatter, "a glob or filter (`*`, `&`, `!&`)"),
Self::EdgeReference => write!(formatter, "an edge reference (`(a -> b)[0]`)"),
Self::SpecialShape(shape) => write!(formatter, "the special shape `{shape}`"),
Self::BlockString => write!(formatter, "a block string (`|md … |`)"),
Self::Null => write!(formatter, "a `null` deletion"),
Self::Array => write!(formatter, "an array value (`[…]`)"),
Self::ArrayMap => write!(formatter, "a map inside an array (`[{{…}}]`)"),
Self::ReservedField(keyword) => write!(formatter, "the field `{keyword}`"),
Self::FieldForm(keyword) => write!(formatter, "this form of the field `{keyword}`"),
Self::DeclarationInEdgeMap => write!(formatter, "a declaration inside an edge map"),
Self::UnmappedShape(shape) => write!(formatter, "the shape `{shape}`"),
Self::UnmappedDirection(direction) => write!(formatter, "`direction: {direction}`"),
Self::ArrowheadForm(operator) => {
let arrow = operator.arrow();
write!(formatter, "the arrowheads of `{arrow}`")
}
Self::FieldOnEdge(keyword) => write!(formatter, "the field `{keyword}` on an edge"),
Self::FieldOnRoot(keyword) => write!(formatter, "the field `{keyword}` on the root"),
Self::UnknownStyleKey(key) => write!(formatter, "the style key `{key}`"),
Self::ContainerBadge => write!(formatter, "`svugy.badge` on a container"),
}
}
}