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