➖ Integrate "SmartDefault" as "AutoDefault" macro
This commit is contained in:
parent
cb38145bf5
commit
9e4f593dbd
51 changed files with 387 additions and 103 deletions
89
helpers/pagetop-macros/src/smart_default/default_attr.rs
Normal file
89
helpers/pagetop-macros/src/smart_default/default_attr.rs
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn::{parse::Error, MetaNameValue};
|
||||
|
||||
use crate::smart_default::util::find_only;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum ConversionStrategy {
|
||||
NoConversion,
|
||||
Into,
|
||||
}
|
||||
|
||||
pub struct DefaultAttr {
|
||||
pub code: Option<TokenStream>,
|
||||
conversion_strategy: Option<ConversionStrategy>,
|
||||
}
|
||||
|
||||
impl DefaultAttr {
|
||||
pub fn find_in_attributes(attrs: &[syn::Attribute]) -> Result<Option<Self>, Error> {
|
||||
if let Some(default_attr) =
|
||||
find_only(attrs.iter(), |attr| Ok(attr.path().is_ident("default")))?
|
||||
{
|
||||
match &default_attr.meta {
|
||||
syn::Meta::Path(_) => Ok(Some(Self {
|
||||
code: None,
|
||||
conversion_strategy: None,
|
||||
})),
|
||||
syn::Meta::List(meta) => {
|
||||
// If the meta contains exactly (_code = "...") take the string literal as the
|
||||
// expression
|
||||
if let Ok(ParseCodeHack(code_hack)) = syn::parse(meta.tokens.clone().into()) {
|
||||
Ok(Some(Self {
|
||||
code: Some(code_hack),
|
||||
conversion_strategy: Some(ConversionStrategy::NoConversion),
|
||||
}))
|
||||
} else {
|
||||
Ok(Some(Self {
|
||||
code: Some(meta.tokens.clone()),
|
||||
conversion_strategy: None,
|
||||
}))
|
||||
}
|
||||
}
|
||||
syn::Meta::NameValue(MetaNameValue { value, .. }) => Ok(Some(Self {
|
||||
code: Some(value.into_token_stream()),
|
||||
conversion_strategy: None,
|
||||
})),
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn conversion_strategy(&self) -> ConversionStrategy {
|
||||
if let Some(conversion_strategy) = self.conversion_strategy {
|
||||
// Conversion strategy already set
|
||||
return conversion_strategy;
|
||||
}
|
||||
let code = if let Some(code) = &self.code {
|
||||
code
|
||||
} else {
|
||||
// #[default] - so no conversion (`Default::default()` already has the correct type)
|
||||
return ConversionStrategy::NoConversion;
|
||||
};
|
||||
match syn::parse::<syn::Lit>(code.clone().into()) {
|
||||
Ok(syn::Lit::Str(_)) | Ok(syn::Lit::ByteStr(_)) => {
|
||||
// A string literal - so we need a conversion in case we need to make it a `String`
|
||||
return ConversionStrategy::Into;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// Not handled by one of the rules, so we don't convert it to avoid causing trouble
|
||||
ConversionStrategy::NoConversion
|
||||
}
|
||||
}
|
||||
|
||||
struct ParseCodeHack(TokenStream);
|
||||
|
||||
impl syn::parse::Parse for ParseCodeHack {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let ident: syn::Ident = input.parse()?;
|
||||
if ident != "_code" {
|
||||
return Err(Error::new(ident.span(), "Expected `_code`"));
|
||||
}
|
||||
input.parse::<syn::token::Eq>()?;
|
||||
let code: syn::LitStr = input.parse()?;
|
||||
let code: TokenStream = code.parse()?;
|
||||
Ok(ParseCodeHack(code))
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue