1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use syn::{self, punctuated::Punctuated};

use generator::Result;
use spanned::Spanned;
use proc_macro::Span;

#[derive(Debug, Copy, Clone)]
pub enum MetaItem<'a> {
    Ident(&'a syn::Ident),
    Literal(&'a syn::Lit),
    KeyValue(&'a syn::Ident, &'a syn::Lit),
    List(MetaItemList<'a>)
}

#[derive(Debug, Copy, Clone)]
pub struct MetaItemList<'a> {
    pub ident: &'a syn::Ident,
    pub iter: &'a Punctuated<syn::NestedMeta, syn::token::Comma>
}

impl<'a> MetaItemList<'a> {
    pub fn iter(&self) -> impl Iterator<Item = MetaItem<'a>> {
        self.iter.iter().map(MetaItem::from)
    }
}

impl<'a> Spanned for MetaItemList<'a> {
    fn span(&self) -> Span {
        self.iter.span()
    }
}

impl<'a> From<&'a syn::Meta> for MetaItem<'a> {
    fn from(meta: &syn::Meta) -> MetaItem {
        match meta {
            syn::Meta::Word(i) => MetaItem::Ident(i),
            syn::Meta::NameValue(nv) => MetaItem::KeyValue(&nv.ident, &nv.lit),
            syn::Meta::List(list) => {
                MetaItem::List(MetaItemList { ident: &list.ident, iter: &list.nested })
            }
        }
    }
}

impl<'a> From<&'a syn::NestedMeta> for MetaItem<'a> {
    fn from(nested: &syn::NestedMeta) -> MetaItem {
        match nested {
            syn::NestedMeta::Meta(meta) => MetaItem::from(meta),
            syn::NestedMeta::Literal(lit) => MetaItem::Literal(lit),
        }
    }
}

impl<'a> MetaItem<'a> {
    pub fn name(&self) -> Option<&syn::Ident> {
        use MetaItem::*;

        match self {
            Ident(i) | KeyValue(i, _) | List(MetaItemList { ident: i, .. }) => {
                Some(i)
            }
            _ => None
        }
    }

    pub fn description(&self) -> &'static str {
        match self {
            MetaItem::Ident(..) => "identifier",
            MetaItem::Literal(syn::Lit::Str(..)) => "string literal",
            MetaItem::Literal(syn::Lit::ByteStr(..)) => "byte string literal",
            MetaItem::Literal(syn::Lit::Byte(..)) => "byte literal",
            MetaItem::Literal(syn::Lit::Char(..)) => "character literal",
            MetaItem::Literal(syn::Lit::Int(..)) => "integer literal",
            MetaItem::Literal(syn::Lit::Float(..)) => "float literal",
            MetaItem::Literal(syn::Lit::Bool(..)) => "boolean literal",
            MetaItem::Literal(syn::Lit::Verbatim(..)) => "literal",
            MetaItem::KeyValue(..) => "key/value pair",
            MetaItem::List(..) => "list",
        }
    }

    pub fn is_bare(&self) -> bool {
        match self {
            MetaItem::Ident(..) | MetaItem::Literal(..) => true,
            MetaItem::KeyValue(..) | MetaItem::List(..) => false,
        }
    }

    pub fn lit(&self) -> Result<&syn::Lit> {
        match self {
            MetaItem::Literal(lit) | MetaItem::KeyValue(_, lit) => Ok(lit),
            _ => Err(self.span().error("expected literal or key/value pair"))
        }
    }

    pub fn value_span(&self) -> Span {
        match self {
            MetaItem::KeyValue(_, lit) => lit.span(),
            _ => self.span(),
        }
    }
}

impl<'a> Spanned for MetaItem<'a> {
    fn span(&self) -> Span {
        match self {
            MetaItem::Ident(i) => i.span(),
            MetaItem::Literal(l) => l.span(),
            MetaItem::KeyValue(i, l) => {
                i.span().join(l.span()).unwrap_or(Span::call_site())
            }
            MetaItem::List(l) => l.span(),
        }
    }
}