aboutsummaryrefslogtreecommitdiff
path: root/vendor/miette-derive/src/diagnostic.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/miette-derive/src/diagnostic.rs')
-rw-r--r--vendor/miette-derive/src/diagnostic.rs397
1 files changed, 397 insertions, 0 deletions
diff --git a/vendor/miette-derive/src/diagnostic.rs b/vendor/miette-derive/src/diagnostic.rs
new file mode 100644
index 0000000..0173d2a
--- /dev/null
+++ b/vendor/miette-derive/src/diagnostic.rs
@@ -0,0 +1,397 @@
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{punctuated::Punctuated, DeriveInput, Token};
+
+use crate::code::Code;
+use crate::diagnostic_arg::DiagnosticArg;
+use crate::diagnostic_source::DiagnosticSource;
+use crate::forward::{Forward, WhichFn};
+use crate::help::Help;
+use crate::label::Labels;
+use crate::related::Related;
+use crate::severity::Severity;
+use crate::source_code::SourceCode;
+use crate::url::Url;
+
+pub enum Diagnostic {
+ Struct {
+ generics: syn::Generics,
+ ident: syn::Ident,
+ fields: syn::Fields,
+ args: DiagnosticDefArgs,
+ },
+ Enum {
+ ident: syn::Ident,
+ generics: syn::Generics,
+ variants: Vec<DiagnosticDef>,
+ },
+}
+
+pub struct DiagnosticDef {
+ pub ident: syn::Ident,
+ pub fields: syn::Fields,
+ pub args: DiagnosticDefArgs,
+}
+
+pub enum DiagnosticDefArgs {
+ Transparent(Forward),
+ Concrete(Box<DiagnosticConcreteArgs>),
+}
+
+impl DiagnosticDefArgs {
+ pub(crate) fn forward_or_override_enum(
+ &self,
+ variant: &syn::Ident,
+ which_fn: WhichFn,
+ mut f: impl FnMut(&DiagnosticConcreteArgs) -> Option<TokenStream>,
+ ) -> Option<TokenStream> {
+ match self {
+ Self::Transparent(forward) => Some(forward.gen_enum_match_arm(variant, which_fn)),
+ Self::Concrete(concrete) => f(concrete).or_else(|| {
+ concrete
+ .forward
+ .as_ref()
+ .map(|forward| forward.gen_enum_match_arm(variant, which_fn))
+ }),
+ }
+ }
+}
+
+#[derive(Default)]
+pub struct DiagnosticConcreteArgs {
+ pub code: Option<Code>,
+ pub severity: Option<Severity>,
+ pub help: Option<Help>,
+ pub labels: Option<Labels>,
+ pub source_code: Option<SourceCode>,
+ pub url: Option<Url>,
+ pub forward: Option<Forward>,
+ pub related: Option<Related>,
+ pub diagnostic_source: Option<DiagnosticSource>,
+}
+
+impl DiagnosticConcreteArgs {
+ fn for_fields(fields: &syn::Fields) -> Result<Self, syn::Error> {
+ let labels = Labels::from_fields(fields)?;
+ let source_code = SourceCode::from_fields(fields)?;
+ let related = Related::from_fields(fields)?;
+ let help = Help::from_fields(fields)?;
+ let diagnostic_source = DiagnosticSource::from_fields(fields)?;
+ Ok(DiagnosticConcreteArgs {
+ code: None,
+ help,
+ related,
+ severity: None,
+ labels,
+ url: None,
+ forward: None,
+ source_code,
+ diagnostic_source,
+ })
+ }
+
+ fn add_args(
+ &mut self,
+ attr: &syn::Attribute,
+ args: impl Iterator<Item = DiagnosticArg>,
+ errors: &mut Vec<syn::Error>,
+ ) {
+ for arg in args {
+ match arg {
+ DiagnosticArg::Transparent => {
+ errors.push(syn::Error::new_spanned(attr, "transparent not allowed"));
+ }
+ DiagnosticArg::Forward(to_field) => {
+ if self.forward.is_some() {
+ errors.push(syn::Error::new_spanned(
+ attr,
+ "forward has already been specified",
+ ));
+ }
+ self.forward = Some(to_field);
+ }
+ DiagnosticArg::Code(new_code) => {
+ if self.code.is_some() {
+ errors.push(syn::Error::new_spanned(
+ attr,
+ "code has already been specified",
+ ));
+ }
+ self.code = Some(new_code);
+ }
+ DiagnosticArg::Severity(sev) => {
+ if self.severity.is_some() {
+ errors.push(syn::Error::new_spanned(
+ attr,
+ "severity has already been specified",
+ ));
+ }
+ self.severity = Some(sev);
+ }
+ DiagnosticArg::Help(hl) => {
+ if self.help.is_some() {
+ errors.push(syn::Error::new_spanned(
+ attr,
+ "help has already been specified",
+ ));
+ }
+ self.help = Some(hl);
+ }
+ DiagnosticArg::Url(u) => {
+ if self.url.is_some() {
+ errors.push(syn::Error::new_spanned(
+ attr,
+ "url has already been specified",
+ ));
+ }
+ self.url = Some(u);
+ }
+ }
+ }
+ }
+}
+
+impl DiagnosticDefArgs {
+ fn parse(
+ _ident: &syn::Ident,
+ fields: &syn::Fields,
+ attrs: &[&syn::Attribute],
+ allow_transparent: bool,
+ ) -> syn::Result<Self> {
+ let mut errors = Vec::new();
+
+ // Handle the only condition where Transparent is allowed
+ if allow_transparent && attrs.len() == 1 {
+ if let Ok(args) =
+ attrs[0].parse_args_with(Punctuated::<DiagnosticArg, Token![,]>::parse_terminated)
+ {
+ if matches!(args.first(), Some(DiagnosticArg::Transparent)) {
+ let forward = Forward::for_transparent_field(fields)?;
+ return Ok(Self::Transparent(forward));
+ }
+ }
+ }
+
+ // Create errors for any appearances of Transparent
+ let error_message = if allow_transparent {
+ "diagnostic(transparent) not allowed in combination with other args"
+ } else {
+ "diagnostic(transparent) not allowed here"
+ };
+ fn is_transparent(d: &DiagnosticArg) -> bool {
+ matches!(d, DiagnosticArg::Transparent)
+ }
+
+ let mut concrete = DiagnosticConcreteArgs::for_fields(fields)?;
+ for attr in attrs {
+ let args =
+ attr.parse_args_with(Punctuated::<DiagnosticArg, Token![,]>::parse_terminated);
+ let args = match args {
+ Ok(args) => args,
+ Err(error) => {
+ errors.push(error);
+ continue;
+ }
+ };
+
+ if args.iter().any(is_transparent) {
+ errors.push(syn::Error::new_spanned(attr, error_message));
+ }
+
+ let args = args
+ .into_iter()
+ .filter(|x| !matches!(x, DiagnosticArg::Transparent));
+
+ concrete.add_args(attr, args, &mut errors);
+ }
+
+ let combined_error = errors.into_iter().reduce(|mut lhs, rhs| {
+ lhs.combine(rhs);
+ lhs
+ });
+ if let Some(error) = combined_error {
+ Err(error)
+ } else {
+ Ok(DiagnosticDefArgs::Concrete(Box::new(concrete)))
+ }
+ }
+}
+
+impl Diagnostic {
+ pub fn from_derive_input(input: DeriveInput) -> Result<Self, syn::Error> {
+ let input_attrs = input
+ .attrs
+ .iter()
+ .filter(|x| x.path().is_ident("diagnostic"))
+ .collect::<Vec<&syn::Attribute>>();
+ Ok(match input.data {
+ syn::Data::Struct(data_struct) => {
+ let args = DiagnosticDefArgs::parse(
+ &input.ident,
+ &data_struct.fields,
+ &input_attrs,
+ true,
+ )?;
+
+ Diagnostic::Struct {
+ fields: data_struct.fields,
+ ident: input.ident,
+ generics: input.generics,
+ args,
+ }
+ }
+ syn::Data::Enum(syn::DataEnum { variants, .. }) => {
+ let mut vars = Vec::new();
+ for var in variants {
+ let mut variant_attrs = input_attrs.clone();
+ variant_attrs
+ .extend(var.attrs.iter().filter(|x| x.path().is_ident("diagnostic")));
+ let args =
+ DiagnosticDefArgs::parse(&var.ident, &var.fields, &variant_attrs, true)?;
+ vars.push(DiagnosticDef {
+ ident: var.ident,
+ fields: var.fields,
+ args,
+ });
+ }
+ Diagnostic::Enum {
+ ident: input.ident,
+ generics: input.generics,
+ variants: vars,
+ }
+ }
+ syn::Data::Union(_) => {
+ return Err(syn::Error::new(
+ input.ident.span(),
+ "Can't derive Diagnostic for Unions",
+ ))
+ }
+ })
+ }
+
+ pub fn gen(&self) -> TokenStream {
+ match self {
+ Self::Struct {
+ ident,
+ fields,
+ generics,
+ args,
+ } => {
+ let (impl_generics, ty_generics, where_clause) = &generics.split_for_impl();
+ match args {
+ DiagnosticDefArgs::Transparent(forward) => {
+ let code_method = forward.gen_struct_method(WhichFn::Code);
+ let help_method = forward.gen_struct_method(WhichFn::Help);
+ let url_method = forward.gen_struct_method(WhichFn::Url);
+ let labels_method = forward.gen_struct_method(WhichFn::Labels);
+ let source_code_method = forward.gen_struct_method(WhichFn::SourceCode);
+ let severity_method = forward.gen_struct_method(WhichFn::Severity);
+ let related_method = forward.gen_struct_method(WhichFn::Related);
+ let diagnostic_source_method =
+ forward.gen_struct_method(WhichFn::DiagnosticSource);
+
+ quote! {
+ impl #impl_generics miette::Diagnostic for #ident #ty_generics #where_clause {
+ #code_method
+ #help_method
+ #url_method
+ #labels_method
+ #severity_method
+ #source_code_method
+ #related_method
+ #diagnostic_source_method
+ }
+ }
+ }
+ DiagnosticDefArgs::Concrete(concrete) => {
+ let forward = |which| {
+ concrete
+ .forward
+ .as_ref()
+ .map(|fwd| fwd.gen_struct_method(which))
+ };
+ let code_body = concrete
+ .code
+ .as_ref()
+ .and_then(|x| x.gen_struct())
+ .or_else(|| forward(WhichFn::Code));
+ let help_body = concrete
+ .help
+ .as_ref()
+ .and_then(|x| x.gen_struct(fields))
+ .or_else(|| forward(WhichFn::Help));
+ let sev_body = concrete
+ .severity
+ .as_ref()
+ .and_then(|x| x.gen_struct())
+ .or_else(|| forward(WhichFn::Severity));
+ let rel_body = concrete
+ .related
+ .as_ref()
+ .and_then(|x| x.gen_struct())
+ .or_else(|| forward(WhichFn::Related));
+ let url_body = concrete
+ .url
+ .as_ref()
+ .and_then(|x| x.gen_struct(ident, fields))
+ .or_else(|| forward(WhichFn::Url));
+ let labels_body = concrete
+ .labels
+ .as_ref()
+ .and_then(|x| x.gen_struct(fields))
+ .or_else(|| forward(WhichFn::Labels));
+ let src_body = concrete
+ .source_code
+ .as_ref()
+ .and_then(|x| x.gen_struct(fields))
+ .or_else(|| forward(WhichFn::SourceCode));
+ let diagnostic_source = concrete
+ .diagnostic_source
+ .as_ref()
+ .and_then(|x| x.gen_struct())
+ .or_else(|| forward(WhichFn::DiagnosticSource));
+ quote! {
+ impl #impl_generics miette::Diagnostic for #ident #ty_generics #where_clause {
+ #code_body
+ #help_body
+ #sev_body
+ #rel_body
+ #url_body
+ #labels_body
+ #src_body
+ #diagnostic_source
+ }
+ }
+ }
+ }
+ }
+ Self::Enum {
+ ident,
+ generics,
+ variants,
+ } => {
+ let (impl_generics, ty_generics, where_clause) = &generics.split_for_impl();
+ let code_body = Code::gen_enum(variants);
+ let help_body = Help::gen_enum(variants);
+ let sev_body = Severity::gen_enum(variants);
+ let labels_body = Labels::gen_enum(variants);
+ let src_body = SourceCode::gen_enum(variants);
+ let rel_body = Related::gen_enum(variants);
+ let url_body = Url::gen_enum(ident, variants);
+ let diagnostic_source_body = DiagnosticSource::gen_enum(variants);
+ quote! {
+ impl #impl_generics miette::Diagnostic for #ident #ty_generics #where_clause {
+ #code_body
+ #help_body
+ #sev_body
+ #labels_body
+ #src_body
+ #rel_body
+ #url_body
+ #diagnostic_source_body
+ }
+ }
+ }
+ }
+ }
+}