diff options
Diffstat (limited to 'vendor/miette-derive/src/url.rs')
-rw-r--r-- | vendor/miette-derive/src/url.rs | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/vendor/miette-derive/src/url.rs b/vendor/miette-derive/src/url.rs new file mode 100644 index 0000000..734d1a4 --- /dev/null +++ b/vendor/miette-derive/src/url.rs @@ -0,0 +1,139 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::{ + parenthesized, + parse::{Parse, ParseStream}, + Fields, Token, +}; + +use crate::{ + diagnostic::{DiagnosticConcreteArgs, DiagnosticDef}, + utils::{display_pat_members, gen_all_variants_with, gen_unused_pat}, +}; +use crate::{ + fmt::{self, Display}, + forward::WhichFn, +}; + +pub enum Url { + Display(Display), + DocsRs, +} + +impl Parse for Url { + fn parse(input: ParseStream) -> syn::Result<Self> { + let ident = input.parse::<syn::Ident>()?; + if ident == "url" { + let la = input.lookahead1(); + if la.peek(syn::token::Paren) { + let content; + parenthesized!(content in input); + if content.peek(syn::LitStr) { + let fmt = content.parse()?; + let args = if content.is_empty() { + TokenStream::new() + } else { + fmt::parse_token_expr(&content, false)? + }; + let display = Display { + fmt, + args, + has_bonus_display: false, + }; + Ok(Url::Display(display)) + } else { + let option = content.parse::<syn::Ident>()?; + if option == "docsrs" { + Ok(Url::DocsRs) + } else { + Err(syn::Error::new(option.span(), "Invalid argument to url() sub-attribute. It must be either a string or a plain `docsrs` identifier")) + } + } + } else { + input.parse::<Token![=]>()?; + Ok(Url::Display(Display { + fmt: input.parse()?, + args: TokenStream::new(), + has_bonus_display: false, + })) + } + } else { + Err(syn::Error::new(ident.span(), "not a url")) + } + } +} + +impl Url { + pub(crate) fn gen_enum( + enum_name: &syn::Ident, + variants: &[DiagnosticDef], + ) -> Option<TokenStream> { + gen_all_variants_with( + variants, + WhichFn::Url, + |ident, fields, DiagnosticConcreteArgs { url, .. }| { + let (pat, fmt, args) = match url.as_ref()? { + // fall through to `_ => None` below + Url::Display(display) => { + let (display_pat, display_members) = display_pat_members(fields); + let (fmt, args) = display.expand_shorthand_cloned(&display_members); + (display_pat, fmt.value(), args) + } + Url::DocsRs => { + let pat = gen_unused_pat(fields); + let fmt = + "https://docs.rs/{crate_name}/{crate_version}/{mod_name}/{item_path}" + .into(); + let item_path = format!("enum.{}.html#variant.{}", enum_name, ident); + let args = quote! { + , + crate_name=env!("CARGO_PKG_NAME"), + crate_version=env!("CARGO_PKG_VERSION"), + mod_name=env!("CARGO_PKG_NAME").replace('-', "_"), + item_path=#item_path + }; + (pat, fmt, args) + } + }; + Some(quote! { + Self::#ident #pat => std::option::Option::Some(std::boxed::Box::new(format!(#fmt #args))), + }) + }, + ) + } + + pub(crate) fn gen_struct( + &self, + struct_name: &syn::Ident, + fields: &Fields, + ) -> Option<TokenStream> { + let (pat, fmt, args) = match self { + Url::Display(display) => { + let (display_pat, display_members) = display_pat_members(fields); + let (fmt, args) = display.expand_shorthand_cloned(&display_members); + (display_pat, fmt.value(), args) + } + Url::DocsRs => { + let pat = gen_unused_pat(fields); + let fmt = + "https://docs.rs/{crate_name}/{crate_version}/{mod_name}/{item_path}".into(); + let item_path = format!("struct.{}.html", struct_name); + let args = quote! { + , + crate_name=env!("CARGO_PKG_NAME"), + crate_version=env!("CARGO_PKG_VERSION"), + mod_name=env!("CARGO_PKG_NAME").replace('-', "_"), + item_path=#item_path + }; + (pat, fmt, args) + } + }; + Some(quote! { + fn url(&self) -> std::option::Option<std::boxed::Box<dyn std::fmt::Display + '_>> { + #[allow(unused_variables, deprecated)] + let Self #pat = self; + std::option::Option::Some(std::boxed::Box::new(format!(#fmt #args))) + } + }) + } +} |