mirror of https://github.com/zkat/miette.git
fix(derive): move to plain syn to fix darling issues
This commit is contained in:
parent
027c3b0a94
commit
9a78a94395
|
|
@ -14,4 +14,3 @@ proc-macro = true
|
|||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
syn = "1.0.45"
|
||||
darling = "0.13.0"
|
||||
|
|
|
|||
|
|
@ -1,39 +1,44 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use darling::{ast::Fields, error::Error as DarlingError, FromMeta};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{Lit, Meta, NestedMeta};
|
||||
use syn::{
|
||||
parenthesized,
|
||||
parse::{Parse, ParseStream},
|
||||
Token,
|
||||
};
|
||||
|
||||
use crate::{Diagnostic, DiagnosticField, DiagnosticVariant};
|
||||
use crate::diagnostic::{Diagnostic, DiagnosticVariant};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Code(String);
|
||||
pub struct Code(pub String);
|
||||
|
||||
impl Display for Code {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMeta for Code {
|
||||
fn from_string(arg: &str) -> Result<Self, DarlingError> {
|
||||
Ok(Code(arg.into()))
|
||||
}
|
||||
|
||||
fn from_list(items: &[NestedMeta]) -> Result<Self, DarlingError> {
|
||||
match &items[0] {
|
||||
NestedMeta::Meta(Meta::Path(p)) => Ok(Code(
|
||||
p.segments
|
||||
.iter()
|
||||
.map(|s| s.ident.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("::"),
|
||||
)),
|
||||
NestedMeta::Lit(Lit::Str(code)) => Ok(Code(code.value())),
|
||||
_ => Err(DarlingError::custom(
|
||||
"invalid code format. Only path::style and string literals are accepted",
|
||||
)),
|
||||
impl Parse for Code {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let ident = input.parse::<syn::Ident>()?;
|
||||
if ident == "code" {
|
||||
let la = input.lookahead1();
|
||||
if la.peek(syn::token::Paren) {
|
||||
let content;
|
||||
parenthesized!(content in input);
|
||||
let la = content.lookahead1();
|
||||
if la.peek(syn::LitStr) {
|
||||
let str = content.parse::<syn::LitStr>()?;
|
||||
Ok(Code(str.value()))
|
||||
} else {
|
||||
let path = content.parse::<syn::Path>()?;
|
||||
Ok(Code(
|
||||
path.segments
|
||||
.iter()
|
||||
.map(|s| s.ident.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("::"),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
input.parse::<Token![=]>()?;
|
||||
Ok(Code(input.parse::<syn::LitStr>()?.value()))
|
||||
}
|
||||
} else {
|
||||
Err(syn::Error::new(ident.span(), "diagnostic code is required. Use #[diagnostic(code = ...)] or #[diagnostic(code(...))] to define one."))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -41,16 +46,25 @@ impl FromMeta for Code {
|
|||
impl Code {
|
||||
pub(crate) fn gen_enum(
|
||||
_diag: &Diagnostic,
|
||||
variants: &[&DiagnosticVariant],
|
||||
variants: &[DiagnosticVariant],
|
||||
) -> Option<TokenStream> {
|
||||
let code_pairs = variants.iter().map(
|
||||
|DiagnosticVariant {
|
||||
ref ident,
|
||||
ref code,
|
||||
ref fields,
|
||||
..
|
||||
}| {
|
||||
let code = code.to_string();
|
||||
quote! { Self::#ident => std::boxed::Box::new(#code), }
|
||||
let code = &code.0;
|
||||
match fields {
|
||||
syn::Fields::Named(_) => {
|
||||
quote! { Self::#ident { .. } => std::boxed::Box::new(#code), }
|
||||
}
|
||||
syn::Fields::Unnamed(_) => {
|
||||
quote! { Self::#ident(..) => std::boxed::Box::new(#code), }
|
||||
}
|
||||
syn::Fields::Unit => quote! { Self::#ident => std::boxed::Box::new(#code), },
|
||||
}
|
||||
},
|
||||
);
|
||||
Some(quote! {
|
||||
|
|
@ -62,15 +76,8 @@ impl Code {
|
|||
})
|
||||
}
|
||||
|
||||
pub(crate) fn gen_struct(
|
||||
diag: &Diagnostic,
|
||||
_fields: &Fields<&DiagnosticField>,
|
||||
) -> Option<TokenStream> {
|
||||
let code = diag
|
||||
.code
|
||||
.as_ref()
|
||||
.expect("`code` attribute is required for diagnostics.")
|
||||
.to_string();
|
||||
pub(crate) fn gen_struct(&self) -> Option<TokenStream> {
|
||||
let code = &self.0;
|
||||
Some(quote! {
|
||||
fn code<'a>(&'a self) -> std::boxed::Box<dyn std::fmt::Display + 'a> {
|
||||
std::boxed::Box::new(#code)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +1,47 @@
|
|||
use darling::{ast::Fields, error::Error as DarlingError, FromMeta};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{Lit, NestedMeta};
|
||||
use syn::{
|
||||
parenthesized,
|
||||
parse::{Parse, ParseStream},
|
||||
Token,
|
||||
};
|
||||
|
||||
use crate::{Diagnostic, DiagnosticField, DiagnosticVariant};
|
||||
use crate::diagnostic::{Diagnostic, DiagnosticVariant};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Help {
|
||||
pub fmt: String,
|
||||
pub args: Vec<NestedMeta>,
|
||||
pub args: Vec<syn::Expr>,
|
||||
}
|
||||
|
||||
impl FromMeta for Help {
|
||||
fn from_string(arg: &str) -> Result<Help, DarlingError> {
|
||||
Ok(Help {
|
||||
fmt: arg.into(),
|
||||
args: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn from_list(items: &[NestedMeta]) -> Result<Help, DarlingError> {
|
||||
match &items.get(0) {
|
||||
Some(NestedMeta::Lit(Lit::Str(fmt))) => Ok(Help {
|
||||
fmt: fmt.value(),
|
||||
args: items[1..]
|
||||
.iter()
|
||||
.map(|item| match item {
|
||||
NestedMeta::Meta(_) => Err(DarlingError::custom(
|
||||
"Only literals are supported for now. Sorry :("
|
||||
)),
|
||||
NestedMeta::Lit(_) => Ok(item.clone()),
|
||||
})
|
||||
.collect::<Result<Vec<_>, DarlingError>>()?,
|
||||
}),
|
||||
None => Err(DarlingError::custom("Help format string is required")),
|
||||
_ => Err(DarlingError::custom(
|
||||
"First argument must be a literal format string",
|
||||
)),
|
||||
impl Parse for Help {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let ident = input.parse::<syn::Ident>()?;
|
||||
if ident == "help" {
|
||||
let la = input.lookahead1();
|
||||
if la.peek(syn::token::Paren) {
|
||||
let content;
|
||||
parenthesized!(content in input);
|
||||
let str = content.parse::<syn::LitStr>()?;
|
||||
Ok(Help {
|
||||
fmt: str.value(),
|
||||
args: Vec::new(),
|
||||
})
|
||||
} else {
|
||||
input.parse::<Token![=]>()?;
|
||||
Ok(Help {
|
||||
fmt: input.parse::<syn::LitStr>()?.value(),
|
||||
args: Vec::new(),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Err(syn::Error::new(ident.span(), "not a help"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Help {
|
||||
pub(crate) fn gen_enum(
|
||||
_diag: &Diagnostic,
|
||||
variants: &[&DiagnosticVariant],
|
||||
variants: &[DiagnosticVariant],
|
||||
) -> Option<TokenStream> {
|
||||
let help_pairs = variants
|
||||
.iter()
|
||||
|
|
@ -53,12 +50,22 @@ impl Help {
|
|||
|DiagnosticVariant {
|
||||
ref ident,
|
||||
ref help,
|
||||
ref fields,
|
||||
..
|
||||
}| {
|
||||
let help = &help.as_ref().unwrap();
|
||||
let fmt = &help.fmt;
|
||||
let args = help.args.iter().map(|arg| quote! { #arg, });
|
||||
quote! { Self::#ident => std::option::Option::Some(std::boxed::Box::new(format!(#fmt, #(#args),*))), }
|
||||
match fields {
|
||||
syn::Fields::Named(_) => {
|
||||
quote! { Self::#ident{..} => std::option::Option::Some(std::boxed::Box::new(format!(#fmt, #(#args),*))), }
|
||||
}
|
||||
syn::Fields::Unnamed(_) => {
|
||||
quote! { Self::#ident(..) => std::option::Option::Some(std::boxed::Box::new(format!(#fmt, #(#args),*))), }
|
||||
}
|
||||
syn::Fields::Unit =>
|
||||
quote! { Self::#ident => std::option::Option::Some(std::boxed::Box::new(format!(#fmt, #(#args),*))), },
|
||||
}
|
||||
},
|
||||
)
|
||||
.collect::<Vec<_>>();
|
||||
|
|
@ -76,17 +83,12 @@ impl Help {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn gen_struct(
|
||||
diag: &Diagnostic,
|
||||
_fields: &Fields<&DiagnosticField>,
|
||||
) -> Option<TokenStream> {
|
||||
diag.help.as_ref().map(|h| {
|
||||
let fmt = &h.fmt;
|
||||
let args = &h.args;
|
||||
quote! {
|
||||
fn help<'a>(&'a self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + 'a>> {
|
||||
std::option::Option::Some(std::boxed::Box::new(format!(#fmt, #(#args),*)))
|
||||
}
|
||||
pub(crate) fn gen_struct(&self) -> Option<TokenStream> {
|
||||
let fmt = &self.fmt;
|
||||
let args = &self.args;
|
||||
Some(quote! {
|
||||
fn help<'a>(&'a self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + 'a>> {
|
||||
std::option::Option::Some(std::boxed::Box::new(format!(#fmt, #(#args),*)))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,101 +1,21 @@
|
|||
use darling::{
|
||||
ast::{self, Fields},
|
||||
FromDeriveInput, FromField, FromVariant, ToTokens,
|
||||
};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
|
||||
use code::Code;
|
||||
use help::Help;
|
||||
use severity::Severity;
|
||||
use diagnostic::Diagnostic;
|
||||
|
||||
mod code;
|
||||
mod diagnostic;
|
||||
mod diagnostic_arg;
|
||||
mod help;
|
||||
mod severity;
|
||||
|
||||
#[proc_macro_derive(Diagnostic, attributes(diagnostic))]
|
||||
pub fn derive_diagnostic(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let cmd = match Diagnostic::from_derive_input(&input) {
|
||||
Ok(cmd) => cmd,
|
||||
Err(err) => return err.write_errors().into(),
|
||||
let cmd = match Diagnostic::from_derive_input(input) {
|
||||
Ok(cmd) => cmd.gen(),
|
||||
Err(err) => return err.to_compile_error().into(),
|
||||
};
|
||||
// panic!("{:#}", cmd.to_token_stream());
|
||||
quote!(#cmd).into()
|
||||
}
|
||||
|
||||
#[derive(Debug, FromDeriveInput)]
|
||||
#[darling(supports(any), attributes(diagnostic))]
|
||||
struct Diagnostic {
|
||||
ident: syn::Ident,
|
||||
data: ast::Data<DiagnosticVariant, DiagnosticField>,
|
||||
generics: syn::Generics,
|
||||
#[darling(default)]
|
||||
code: Option<Code>,
|
||||
#[darling(default)]
|
||||
severity: Option<Severity>,
|
||||
#[darling(default)]
|
||||
help: Option<Help>,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromField)]
|
||||
struct DiagnosticField {
|
||||
ident: Option<syn::Ident>,
|
||||
ty: syn::Type,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromVariant)]
|
||||
#[darling(attributes(diagnostic))]
|
||||
struct DiagnosticVariant {
|
||||
ident: syn::Ident,
|
||||
code: Code,
|
||||
#[darling(default)]
|
||||
severity: Option<Severity>,
|
||||
#[darling(default)]
|
||||
help: Option<Help>,
|
||||
}
|
||||
|
||||
impl ToTokens for Diagnostic {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let ts = match self.data.as_ref() {
|
||||
ast::Data::Enum(variants) => self.gen_enum(variants),
|
||||
ast::Data::Struct(fields) => self.gen_struct(fields),
|
||||
};
|
||||
tokens.extend(ts);
|
||||
}
|
||||
}
|
||||
|
||||
impl Diagnostic {
|
||||
fn gen_enum(&self, variants: Vec<&DiagnosticVariant>) -> TokenStream {
|
||||
let ident = &self.ident;
|
||||
let (impl_generics, ty_generics, where_clause) = &self.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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_struct(&self, fields: Fields<&DiagnosticField>) -> TokenStream {
|
||||
let ident= &self.ident;
|
||||
let (impl_generics, ty_generics, where_clause) = &self.generics.split_for_impl();
|
||||
let code_body = Code::gen_struct(self, &fields);
|
||||
let help_body = Help::gen_struct(self, &fields);
|
||||
let sev_body = Severity::gen_struct(self, &fields);
|
||||
|
||||
quote! {
|
||||
impl #impl_generics miette::Diagnostic for #ident #ty_generics #where_clause {
|
||||
#code_body
|
||||
#help_body
|
||||
#sev_body
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +1,47 @@
|
|||
use darling::{ast::Fields, error::Error as DarlingError, FromMeta};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{Lit, LitStr, Meta, NestedMeta, Path};
|
||||
use syn::{
|
||||
parenthesized,
|
||||
parse::{Parse, ParseStream},
|
||||
Token,
|
||||
};
|
||||
|
||||
use crate::{Diagnostic, DiagnosticField, DiagnosticVariant};
|
||||
use crate::diagnostic::{Diagnostic, DiagnosticVariant};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Severity(pub Path);
|
||||
pub struct Severity(pub syn::Path);
|
||||
|
||||
impl FromMeta for Severity {
|
||||
fn from_string(arg: &str) -> Result<Self, DarlingError> {
|
||||
Ok(Severity(LitStr::new(arg, Span::call_site()).parse()?))
|
||||
}
|
||||
|
||||
fn from_list(items: &[NestedMeta]) -> Result<Self, DarlingError> {
|
||||
match &items[0] {
|
||||
NestedMeta::Meta(Meta::Path(p)) => Ok(Severity(p.clone())),
|
||||
NestedMeta::Lit(Lit::Str(sev)) => Ok(Severity(sev.parse()?)),
|
||||
_ => Err(DarlingError::custom(
|
||||
"invalid severity format. Only literal names and string literals are accepted",
|
||||
)),
|
||||
impl Parse for Severity {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let ident = input.parse::<syn::Ident>()?;
|
||||
if ident == "severity" {
|
||||
let la = input.lookahead1();
|
||||
if la.peek(syn::token::Paren) {
|
||||
let content;
|
||||
parenthesized!(content in input);
|
||||
let la = content.lookahead1();
|
||||
if la.peek(syn::LitStr) {
|
||||
let str = content.parse::<syn::LitStr>()?;
|
||||
Ok(Severity(str.parse()?))
|
||||
} else {
|
||||
let path = content.parse::<syn::Path>()?;
|
||||
Ok(Severity(path))
|
||||
}
|
||||
} else {
|
||||
input.parse::<Token![=]>()?;
|
||||
Ok(Severity(input.parse::<syn::LitStr>()?.parse()?))
|
||||
}
|
||||
} else {
|
||||
Err(syn::Error::new(
|
||||
ident.span(),
|
||||
"not a severity level.",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Severity {
|
||||
pub(crate) fn gen_enum(
|
||||
_diag: &Diagnostic,
|
||||
variants: &[&DiagnosticVariant],
|
||||
variants: &[DiagnosticVariant],
|
||||
) -> Option<TokenStream> {
|
||||
let sev_pairs = variants
|
||||
.iter()
|
||||
|
|
@ -55,13 +69,11 @@ impl Severity {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn gen_struct(diag: &Diagnostic, _fields: &Fields<&DiagnosticField>) -> Option<TokenStream> {
|
||||
diag.severity.as_ref().map(|sev| {
|
||||
let sev = &sev.0;
|
||||
quote! {
|
||||
fn severity(&self) -> std::option::Option<miette::Severity> {
|
||||
Some(miette::Severity::#sev)
|
||||
}
|
||||
pub(crate) fn gen_struct(&self) -> Option<TokenStream> {
|
||||
let sev = &self.0;
|
||||
Some(quote! {
|
||||
fn severity(&self) -> std::option::Option<miette::Severity> {
|
||||
Some(miette::Severity::#sev)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ fn basic_struct() {
|
|||
|
||||
#[test]
|
||||
fn basic_enum() {
|
||||
|
||||
#[derive(Debug, Diagnostic, Error)]
|
||||
#[error("welp")]
|
||||
enum Foo {
|
||||
|
|
@ -35,14 +34,39 @@ fn basic_enum() {
|
|||
)]
|
||||
X,
|
||||
#[diagnostic(code = "foo::y")]
|
||||
Y,
|
||||
Y(usize),
|
||||
#[diagnostic(code = "foo::z")]
|
||||
Z { prop: String },
|
||||
}
|
||||
|
||||
assert_eq!("foo::x".to_string(), Foo::X.code().to_string());
|
||||
assert_eq!("foo::y".to_string(), Foo::Y.code().to_string());
|
||||
assert_eq!("foo::y".to_string(), Foo::Y(1).code().to_string());
|
||||
assert_eq!(
|
||||
"foo::z".to_string(),
|
||||
Foo::Z { prop: "bar".into() }.code().to_string()
|
||||
);
|
||||
|
||||
assert_eq!(Some(Severity::Warning), Foo::X.severity());
|
||||
assert_eq!(None, Foo::Y.severity());
|
||||
assert_eq!(None, Foo::Y(1).severity());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn paren_code() {
|
||||
#[derive(Debug, Diagnostic, Error)]
|
||||
#[error("welp")]
|
||||
#[diagnostic(code("foo::bar::baz"))]
|
||||
struct FooStruct;
|
||||
|
||||
assert_eq!("foo::bar::baz".to_string(), FooStruct.code().to_string());
|
||||
|
||||
#[derive(Debug, Diagnostic, Error)]
|
||||
#[error("welp")]
|
||||
enum FooEnum {
|
||||
#[diagnostic(code("foo::x"))]
|
||||
X,
|
||||
}
|
||||
|
||||
assert_eq!("foo::x".to_string(), FooEnum::X.code().to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -68,10 +92,7 @@ fn path_code() {
|
|||
fn path_severity() {
|
||||
#[derive(Debug, Diagnostic, Error)]
|
||||
#[error("welp")]
|
||||
#[diagnostic(
|
||||
code(foo::bar::baz),
|
||||
severity(Warning)
|
||||
)]
|
||||
#[diagnostic(code(foo::bar::baz), severity(Warning))]
|
||||
struct FooStruct;
|
||||
|
||||
assert_eq!(Some(Severity::Warning), FooStruct.severity());
|
||||
|
|
@ -79,10 +100,7 @@ fn path_severity() {
|
|||
#[derive(Debug, Diagnostic, Error)]
|
||||
#[error("welp")]
|
||||
enum FooEnum {
|
||||
#[diagnostic(
|
||||
code(foo::x),
|
||||
severity(Warning),
|
||||
)]
|
||||
#[diagnostic(code(foo::x), severity(Warning))]
|
||||
X,
|
||||
}
|
||||
|
||||
|
|
@ -93,10 +111,7 @@ fn path_severity() {
|
|||
fn list_help() {
|
||||
#[derive(Debug, Diagnostic, Error)]
|
||||
#[error("welp")]
|
||||
#[diagnostic(
|
||||
code(foo::bar::baz),
|
||||
help("try doing it better"),
|
||||
)]
|
||||
#[diagnostic(code(foo::bar::baz), help("try doing it better"))]
|
||||
struct FooStruct;
|
||||
|
||||
assert_eq!(
|
||||
|
|
@ -107,10 +122,7 @@ fn list_help() {
|
|||
#[derive(Debug, Diagnostic, Error)]
|
||||
#[error("welp")]
|
||||
enum FooEnum {
|
||||
#[diagnostic(
|
||||
code(foo::x),
|
||||
help("try doing it better"),
|
||||
)]
|
||||
#[diagnostic(code(foo::x), help("try doing it better"))]
|
||||
X,
|
||||
}
|
||||
|
||||
|
|
@ -120,8 +132,6 @@ fn list_help() {
|
|||
);
|
||||
}
|
||||
|
||||
// TODO: Darling doesn't support this, apparently:
|
||||
// https://github.com/TedDriggs/darling/issues/145
|
||||
/*
|
||||
#[test]
|
||||
fn fmt_help() {
|
||||
|
|
@ -129,9 +139,9 @@ fn fmt_help() {
|
|||
#[error("welp")]
|
||||
#[diagnostic(
|
||||
code(foo::bar::baz),
|
||||
help("{} {}", 1, "bar"),
|
||||
help("{} {}", 1, self.0),
|
||||
)]
|
||||
struct FooStruct;
|
||||
struct FooStruct(String);
|
||||
|
||||
assert_eq!(
|
||||
"1 bar".to_string(),
|
||||
|
|
|
|||
Loading…
Reference in New Issue