-
-
Notifications
You must be signed in to change notification settings - Fork 120
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(derive): move to plain syn to fix darling issues
- Loading branch information
Showing
8 changed files
with
376 additions
and
225 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,4 +14,3 @@ proc-macro = true | |
proc-macro2 = "1.0" | ||
quote = "1.0" | ||
syn = "1.0.45" | ||
darling = "0.13.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
use proc_macro2::TokenStream; | ||
use quote::quote; | ||
use syn::{punctuated::Punctuated, DeriveInput, Token}; | ||
|
||
use crate::code::Code; | ||
use crate::diagnostic_arg::DiagnosticArg; | ||
use crate::help::Help; | ||
use crate::severity::Severity; | ||
|
||
pub enum Diagnostic { | ||
Struct { | ||
ident: syn::Ident, | ||
generics: syn::Generics, | ||
code: Code, | ||
severity: Option<Severity>, | ||
help: Option<Help>, | ||
}, | ||
Enum { | ||
ident: syn::Ident, | ||
generics: syn::Generics, | ||
variants: Vec<DiagnosticVariant>, | ||
}, | ||
} | ||
|
||
pub struct DiagnosticVariant { | ||
pub ident: syn::Ident, | ||
pub fields: syn::Fields, | ||
pub code: Code, | ||
pub severity: Option<Severity>, | ||
pub help: Option<Help>, | ||
} | ||
|
||
impl Diagnostic { | ||
pub fn from_derive_input(input: DeriveInput) -> Result<Self, syn::Error> { | ||
Ok(match input.data { | ||
syn::Data::Struct(_) => { | ||
if let Some(attr) = input.attrs.iter().find(|x| x.path.is_ident("diagnostic")) { | ||
let args = attr.parse_args_with( | ||
Punctuated::<DiagnosticArg, Token![,]>::parse_terminated, | ||
)?; | ||
let mut code = None; | ||
let mut severity = None; | ||
let mut help = None; | ||
for arg in args { | ||
match arg { | ||
DiagnosticArg::Code(new_code) => { | ||
// TODO: error on multiple? | ||
code = Some(new_code); | ||
} | ||
DiagnosticArg::Severity(sev) => { | ||
severity = Some(sev); | ||
} | ||
DiagnosticArg::Help(hl) => { | ||
help = Some(hl) | ||
} | ||
} | ||
} | ||
let ident = input.ident.clone(); | ||
Diagnostic::Struct { | ||
ident: input.ident, | ||
generics: input.generics, | ||
code: code.ok_or_else(|| { | ||
syn::Error::new(ident.span(), "Diagnostic code is required.") | ||
})?, | ||
help, | ||
severity, | ||
} | ||
} else { | ||
// Also handle when there's multiple `#[diagnostic]` attrs? | ||
return Err(syn::Error::new( | ||
input.ident.span(), | ||
"#[diagnostic] attribute is required when deriving Diagnostic.", | ||
)); | ||
} | ||
} | ||
syn::Data::Enum(syn::DataEnum { variants, .. }) => { | ||
let mut vars = Vec::new(); | ||
for var in variants { | ||
if let Some(attr) = var.attrs.iter().find(|x| x.path.is_ident("diagnostic")) { | ||
let args = attr.parse_args_with( | ||
Punctuated::<DiagnosticArg, Token![,]>::parse_terminated, | ||
)?; | ||
let mut code = None; | ||
let mut severity = None; | ||
let mut help = None; | ||
for arg in args { | ||
match arg { | ||
DiagnosticArg::Code(new_code) => { | ||
// TODO: error on multiple? | ||
code = Some(new_code); | ||
} | ||
DiagnosticArg::Severity(sev) => { | ||
severity = Some(sev); | ||
} | ||
DiagnosticArg::Help(hl) => { | ||
help = Some(hl); | ||
} | ||
} | ||
} | ||
let ident = input.ident.clone(); | ||
vars.push(DiagnosticVariant { | ||
ident: var.ident, | ||
fields: var.fields, | ||
code: code.ok_or_else(|| { | ||
syn::Error::new(ident.span(), "Diagnostic code is required.") | ||
})?, | ||
help, | ||
severity, | ||
}); | ||
} else { | ||
// Also handle when there's multiple `#[diagnostic]` attrs? | ||
return Err(syn::Error::new( | ||
var.ident.span(), | ||
"#[diagnostic] attribute is required on all enum variants when deriving Diagnostic.", | ||
)); | ||
} | ||
} | ||
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, | ||
generics, | ||
code, | ||
severity, | ||
help, | ||
} => { | ||
let (impl_generics, ty_generics, where_clause) = &generics.split_for_impl(); | ||
let code_body = code.gen_struct(); | ||
let help_body = help.as_ref().and_then(|x| x.gen_struct()); | ||
let sev_body = severity.as_ref().and_then(|x| x.gen_struct()); | ||
|
||
quote! { | ||
impl #impl_generics miette::Diagnostic for #ident #ty_generics #where_clause { | ||
#code_body | ||
#help_body | ||
#sev_body | ||
} | ||
} | ||
} | ||
Self::Enum { | ||
ident, | ||
generics, | ||
variants, | ||
} => { | ||
let (impl_generics, ty_generics, where_clause) = &generics.split_for_impl(); | ||
let code_body = Code::gen_enum(self, variants); | ||
let help_body = Help::gen_enum(self, variants); | ||
let sev_body = Severity::gen_enum(self, variants); | ||
|
||
quote! { | ||
impl #impl_generics miette::Diagnostic for #ident #ty_generics #where_clause { | ||
#code_body | ||
#help_body | ||
#sev_body | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
use syn::parse::{Parse, ParseStream}; | ||
|
||
use crate::code::Code; | ||
use crate::help::Help; | ||
use crate::severity::Severity; | ||
|
||
pub enum DiagnosticArg { | ||
Code(Code), | ||
Severity(Severity), | ||
Help(Help), | ||
} | ||
|
||
impl Parse for DiagnosticArg { | ||
fn parse(input: ParseStream) -> syn::Result<Self> { | ||
let ident = input.fork().parse::<syn::Ident>()?; | ||
if ident == "code" { | ||
Ok(DiagnosticArg::Code(input.parse()?)) | ||
} else if ident == "severity" { | ||
Ok(DiagnosticArg::Severity(input.parse()?)) | ||
} else if ident == "help" { | ||
Ok(DiagnosticArg::Help(input.parse()?)) | ||
} else { | ||
Err(syn::Error::new(ident.span(), "Unrecognized diagnostic option")) | ||
} | ||
} | ||
} |
Oops, something went wrong.