1use super::ValidationError;
5use miette::{Diagnostic, NamedSource, SourceSpan};
6
7#[derive(Debug)]
13pub struct ValidationDiagnostic {
14 pub error: ValidationError,
16 pub src: Option<NamedSource<String>>,
19 pub span: Option<SourceSpan>,
22}
23
24impl std::fmt::Display for ValidationDiagnostic {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 std::fmt::Display::fmt(&self.error, f)
27 }
28}
29
30impl std::error::Error for ValidationDiagnostic {
31 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
32 self.error.source()
33 }
34}
35
36impl Diagnostic for ValidationDiagnostic {
37 fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
38 self.error.code()
39 }
40
41 fn severity(&self) -> Option<miette::Severity> {
42 self.error.severity()
43 }
44
45 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
46 self.error.help()
47 }
48
49 fn url<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
50 self.error.url()
51 }
52
53 fn source_code(&self) -> Option<&dyn miette::SourceCode> {
54 self.src
55 .as_ref()
56 .map(|s| s as &dyn miette::SourceCode)
57 .or_else(|| self.error.source_code())
58 }
59
60 fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
61 if let Some(span) = self.span {
62 Some(Box::new(std::iter::once(
63 miette::LabeledSpan::new_with_span(Some("here".to_string()), span),
64 )))
65 } else {
66 self.error.labels()
67 }
68 }
69}
70
71impl ValidationDiagnostic {
72 pub fn new(error: ValidationError, source: Option<(&str, &str)>) -> Self {
80 let (src, span) = match source {
81 Some((filename, contents)) => {
82 let span = find_offending_span(&error, contents);
83 (Some(NamedSource::new(filename, contents.to_string())), span)
84 }
85 None => (None, None),
86 };
87 Self { error, src, span }
88 }
89}
90
91fn find_offending_span(err: &ValidationError, src: &str) -> Option<SourceSpan> {
92 let needle: &str = match err {
93 ValidationError::DuplicateModuleName(n) => Some(n.as_str()),
94 ValidationError::InvalidModuleName(n, _) => Some(n.as_str()),
95 ValidationError::DuplicateFunctionName { function, .. } => Some(function.as_str()),
96 ValidationError::DuplicateParamName { param, .. } => Some(param.as_str()),
97 ValidationError::ReservedKeyword(n) => Some(n.as_str()),
98 ValidationError::InvalidIdentifier(n, _) => Some(n.as_str()),
99 ValidationError::DuplicateErrorName { name, .. } => Some(name.as_str()),
100 ValidationError::InvalidErrorCode { name, .. } => Some(name.as_str()),
101 ValidationError::NameCollisionWithErrorDomain { name, .. } => Some(name.as_str()),
102 ValidationError::DuplicateStructName { name, .. } => Some(name.as_str()),
103 ValidationError::DuplicateStructField { field, .. } => Some(field.as_str()),
104 ValidationError::EmptyStruct { name, .. } => Some(name.as_str()),
105 ValidationError::DuplicateEnumName { name, .. } => Some(name.as_str()),
106 ValidationError::EmptyEnum { name, .. } => Some(name.as_str()),
107 ValidationError::DuplicateEnumVariant { variant, .. } => Some(variant.as_str()),
108 ValidationError::UnknownTypeRef { name } => Some(name.as_str()),
109 ValidationError::DuplicateCallbackName { name, .. } => Some(name.as_str()),
110 ValidationError::UnsupportedCallbackParamType { param, .. } => Some(param.as_str()),
111 ValidationError::ListenerCallbackNotFound { callback, .. } => Some(callback.as_str()),
112 ValidationError::DuplicateListenerName { name, .. } => Some(name.as_str()),
113 ValidationError::BuilderStructEmpty { name, .. } => Some(name.as_str()),
114 ValidationError::UnsupportedSchemaVersion { version, .. } => Some(version.as_str()),
115 ValidationError::AsyncIteratorReturn { function, .. } => Some(function.as_str()),
116 ValidationError::DuplicateInterfaceName { name, .. } => Some(name.as_str()),
117 ValidationError::DuplicateInterfaceMember { name, .. } => Some(name.as_str()),
118 ValidationError::EmptyInterface { name, .. } => Some(name.as_str()),
119 ValidationError::ConstructorHasReturn { constructor, .. } => Some(constructor.as_str()),
120 ValidationError::AsyncConstructor { constructor, .. } => Some(constructor.as_str()),
121 ValidationError::InterfaceInInvalidPosition { name, .. } => Some(name.as_str()),
122 ValidationError::DuplicateTypeName { name, .. } => Some(name.as_str()),
123 ValidationError::DuplicateErrorCodeName { name, .. } => Some(name.as_str()),
124 ValidationError::ThrowsWithoutErrorDomain { function, .. } => Some(function.as_str()),
125 ValidationError::AbiSymbolCollision { symbol, .. } => Some(symbol.as_str()),
126 _ => None,
127 }?;
128 let quoted = format!("\"{needle}\"");
129 if let Some(pos) = src.find("ed) {
130 return Some(SourceSpan::new(pos.into(), quoted.len()));
131 }
132 src.find(needle)
133 .map(|pos| SourceSpan::new(pos.into(), needle.len()))
134}