use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationResult {
pub valid: bool,
pub diagnostics: Vec<Diagnostic>,
}
impl ValidationResult {
#[must_use]
pub fn valid() -> Self {
Self {
valid: true,
diagnostics: Vec::new(),
}
}
#[must_use]
pub fn invalid(diagnostics: Vec<Diagnostic>) -> Self {
Self {
valid: false,
diagnostics,
}
}
#[must_use]
pub fn is_valid(&self) -> bool {
self.valid && !self.has_errors()
}
#[must_use]
pub fn has_errors(&self) -> bool {
self.diagnostics
.iter()
.any(|d| d.severity == DiagnosticSeverity::Error)
}
#[must_use]
pub fn has_warnings(&self) -> bool {
self.diagnostics
.iter()
.any(|d| d.severity == DiagnosticSeverity::Warning)
}
#[must_use]
pub fn diagnostics(&self) -> &[Diagnostic] {
&self.diagnostics
}
pub fn errors(&self) -> impl Iterator<Item = &Diagnostic> {
self.diagnostics
.iter()
.filter(|d| d.severity == DiagnosticSeverity::Error)
}
pub fn warnings(&self) -> impl Iterator<Item = &Diagnostic> {
self.diagnostics
.iter()
.filter(|d| d.severity == DiagnosticSeverity::Warning)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Diagnostic {
pub message: String,
pub severity: DiagnosticSeverity,
pub start: usize,
pub end: usize,
pub line: usize,
pub column: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<String>,
}
impl Diagnostic {
#[must_use]
pub fn length(&self) -> usize {
self.end.saturating_sub(self.start)
}
#[must_use]
pub fn is_error(&self) -> bool {
self.severity == DiagnosticSeverity::Error
}
#[must_use]
pub fn is_warning(&self) -> bool {
self.severity == DiagnosticSeverity::Warning
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub enum DiagnosticSeverity {
Error,
Warning,
Information,
Hint,
}
impl DiagnosticSeverity {
#[allow(dead_code)]
#[must_use]
pub fn parse(s: &str) -> Self {
match s.to_lowercase().as_str() {
"warning" => Self::Warning,
"information" | "info" => Self::Information,
"hint" | "suggestion" => Self::Hint,
_ => Self::Error,
}
}
}
impl std::fmt::Display for DiagnosticSeverity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Error => write!(f, "Error"),
Self::Warning => write!(f, "Warning"),
Self::Information => write!(f, "Information"),
Self::Hint => write!(f, "Hint"),
}
}
}