248 lines
6.1 KiB
Rust
248 lines
6.1 KiB
Rust
use syn;
|
|
use proc_macro2;
|
|
use quote::{quote, format_ident};
|
|
|
|
#[derive(Debug)]
|
|
pub struct Global {
|
|
fallback: Option<String>,
|
|
path: Option<String>,
|
|
name: syn::Ident,
|
|
}
|
|
|
|
impl Global {
|
|
pub fn from_ast(ast: &syn::DeriveInput) -> Self {
|
|
let mut fallback = None;
|
|
let mut path = None;
|
|
|
|
let meta_iter = ast.attrs.iter()
|
|
.filter_map(get_meta_item)
|
|
.flatten();
|
|
|
|
for meta in meta_iter {
|
|
match meta {
|
|
syn::Meta::NameValue(ref m) if m.path.is_ident("fallback") => {
|
|
fallback = get_lit_str(&m.lit);
|
|
},
|
|
syn::Meta::NameValue(ref m) if m.path.is_ident("path") => {
|
|
path = get_lit_str(&m.lit);
|
|
},
|
|
_ => ()
|
|
}
|
|
}
|
|
|
|
Global {
|
|
fallback,
|
|
path,
|
|
name: ast.ident.clone()
|
|
}
|
|
}
|
|
|
|
pub fn gen_use_bind_conf(&self) -> proc_macro2::TokenStream {
|
|
// FIXME: doesn't work with multiple segments path like my_crate::bind_conf
|
|
let path = format_ident!("{}", self.path.as_ref().unwrap_or(&"bind_conf".into()));
|
|
|
|
quote! {
|
|
use #path::{Parser, Error, Result};
|
|
}
|
|
}
|
|
|
|
pub fn gen_enum_fallback(&self) -> proc_macro2::TokenStream {
|
|
if let Some(ref fallback) = self.fallback {
|
|
let fallback_variant = format_ident!("{}", fallback);
|
|
quote! {
|
|
{
|
|
pa.discard_statement()?;
|
|
Ok(Self::#fallback_variant)
|
|
}
|
|
}
|
|
} else {
|
|
quote! {
|
|
Err(Error::UnknownVariant)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn name(&self) -> &syn::Ident {
|
|
&self.name
|
|
}
|
|
}
|
|
|
|
pub trait NamedIdent {
|
|
fn alias(&self) -> Vec<String>;
|
|
fn rename(&self) -> Option<String>;
|
|
fn rename_ident(ident: &syn::Ident) -> String;
|
|
|
|
fn names(&self, ident: &syn::Ident) -> Vec<String> {
|
|
let mut names = self.alias();
|
|
|
|
let name = if let Some(new_name) = self.rename() {
|
|
new_name
|
|
} else {
|
|
Self::rename_ident(ident)
|
|
};
|
|
|
|
if !names.contains(&name) {
|
|
names.push(name.to_string());
|
|
}
|
|
names
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct FieldAttr {
|
|
inline: bool,
|
|
rename: Option<String>,
|
|
alias: Vec<String>,
|
|
default: Option<syn::ExprPath>,
|
|
}
|
|
|
|
impl NamedIdent for FieldAttr {
|
|
fn rename(&self) -> Option<String> {
|
|
self.rename.clone()
|
|
}
|
|
|
|
fn alias(&self) -> Vec<String> {
|
|
self.alias.clone()
|
|
}
|
|
|
|
fn rename_ident(ident: &syn::Ident) -> String {
|
|
ident.to_string().as_str().replace("_", "-").into()
|
|
}
|
|
}
|
|
|
|
impl FieldAttr {
|
|
pub fn from_ast(field: &syn::Field) -> Self {
|
|
|
|
let mut inline = false;
|
|
let mut rename = None;
|
|
let mut alias = Vec::new();
|
|
let mut default = None;
|
|
|
|
let meta_iter = field.attrs.iter()
|
|
.filter_map(get_meta_item)
|
|
.flatten();
|
|
|
|
for meta in meta_iter {
|
|
match meta {
|
|
syn::Meta::Path(ref p) if p.is_ident("inline") => {
|
|
inline = true;
|
|
},
|
|
syn::Meta::NameValue(ref m) if m.path.is_ident("rename") => {
|
|
rename = get_lit_str(&m.lit);
|
|
},
|
|
syn::Meta::NameValue(ref m) if m.path.is_ident("alias") => {
|
|
if let Some(s) = get_lit_str(&m.lit) {
|
|
alias.push(s);
|
|
}
|
|
},
|
|
syn::Meta::NameValue(ref m) if m.path.is_ident("default") => {
|
|
default = get_lit_str(&m.lit).map(|s| syn::parse_str(s.as_ref()).ok()).flatten();
|
|
}
|
|
_ => ()
|
|
}
|
|
}
|
|
|
|
FieldAttr {
|
|
inline,
|
|
rename,
|
|
alias,
|
|
default,
|
|
}
|
|
}
|
|
|
|
pub fn inline(&self) -> bool {
|
|
self.inline
|
|
}
|
|
|
|
pub fn default(&self) -> Option<syn::ExprPath> {
|
|
self.default.clone()
|
|
}
|
|
}
|
|
|
|
pub struct VariantAttr {
|
|
rename: Option<String>,
|
|
alias: Vec<String>,
|
|
}
|
|
|
|
impl NamedIdent for VariantAttr {
|
|
fn rename(&self) -> Option<String> {
|
|
self.rename.clone()
|
|
}
|
|
|
|
fn alias(&self) -> Vec<String> {
|
|
self.alias.clone()
|
|
}
|
|
|
|
fn rename_ident(ident: &syn::Ident) -> String {
|
|
let ident_name = ident.to_string();
|
|
let mut res = String::new();
|
|
|
|
for (i, ch) in ident_name.char_indices() {
|
|
if i > 0 && ch.is_ascii_uppercase() {
|
|
res.push('-');
|
|
}
|
|
|
|
res.push(ch.to_ascii_lowercase());
|
|
}
|
|
|
|
res
|
|
}
|
|
}
|
|
|
|
impl VariantAttr {
|
|
pub fn from_ast(variant: &syn::Variant) -> Self {
|
|
let mut rename = None;
|
|
let mut alias = Vec::new();
|
|
|
|
let meta_iter = variant.attrs.iter()
|
|
.filter_map(get_meta_item)
|
|
.flatten();
|
|
|
|
for meta in meta_iter {
|
|
match meta {
|
|
syn::Meta::NameValue(ref m) if m.path.is_ident("rename") => {
|
|
rename = get_lit_str(&m.lit);
|
|
},
|
|
syn::Meta::NameValue(ref m) if m.path.is_ident("alias") => {
|
|
if let Some(s) = get_lit_str(&m.lit) {
|
|
alias.push(s);
|
|
}
|
|
}
|
|
_ => ()
|
|
}
|
|
}
|
|
|
|
VariantAttr {
|
|
rename,
|
|
alias,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_meta_item(attr: &syn::Attribute) -> Option<Vec<syn::Meta>> {
|
|
if attr.path.is_ident("parse") {
|
|
match attr.parse_meta() {
|
|
Ok(syn::Meta::List(ref meta_list)) => Some(
|
|
meta_list.nested.iter().filter_map(|nested| {
|
|
match nested {
|
|
syn::NestedMeta::Meta(meta) => Some(meta),
|
|
_ => None,
|
|
}
|
|
}).cloned().collect()
|
|
),
|
|
_ => None
|
|
}
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
|
|
fn get_lit_str(lit: &syn::Lit) -> Option<String> {
|
|
if let syn::Lit::Str(lit_str) = lit {
|
|
Some(lit_str.value())
|
|
} else {
|
|
None
|
|
}
|
|
}
|