ligen_core/ir/
attribute.rs

1use crate::ir::Identifier;
2use crate::ir::Literal;
3use crate::prelude::*;
4use crate::proc_macro;
5use proc_macro2::TokenStream;
6use quote::{quote, ToTokens, TokenStreamExt};
7use std::convert::{TryFrom, TryInto};
8use syn::{
9    parse::{Parse, ParseStream},
10    parse2, AttributeArgs, Meta, MetaList, MetaNameValue, NestedMeta, Path, Token,
11};
12
13/// Attribute Enum
14#[derive(Debug, PartialEq, Clone)]
15pub enum Attribute {
16    /// Literal Variant
17    Literal(Literal),
18    /// Named Variant
19    Named(Identifier, Literal),
20    /// Group Variant
21    Group(Identifier, Attributes),
22}
23
24#[derive(Shrinkwrap, Default, Debug, PartialEq, Clone)]
25#[shrinkwrap(mutable)]
26/// Attributes Struct
27pub struct Attributes {
28    /// attributes field
29    pub attributes: Vec<Attribute>,
30}
31
32impl Attributes {
33    /// Get named attribute e.g.: (name = "literal")
34    pub fn get_named(&self, name: &str) -> Option<Literal> {
35        self
36            .attributes
37            .iter()
38            .find_map(|attribute| {
39                if let Attribute::Named(identifier, literal) = attribute {
40                    if identifier.name == name {
41                        Some(literal.clone())
42                    } else {
43                        None
44                    }
45                } else {
46                    None
47                }
48            })
49    }
50}
51
52impl TryFrom<TokenStream> for Attributes {
53    type Error = Error;
54    fn try_from(tokenstream: TokenStream) -> Result<Self> {
55        parse2::<Attributes>(tokenstream).map_err(|_| "Failed to parse Attributes".into())
56    }
57}
58
59impl TryFrom<proc_macro::TokenStream> for Attributes {
60    type Error = Error;
61    fn try_from(tokenstream: proc_macro::TokenStream) -> Result<Self> {
62        let tokenstream: TokenStream = tokenstream.into();
63        tokenstream.try_into()
64    }
65}
66
67impl From<AttributeArgs> for Attributes {
68    fn from(attribute_args: AttributeArgs) -> Self {
69        let attributes = attribute_args
70            .iter()
71            .map(|nested_meta| Attribute::from(nested_meta.clone()))
72            .collect();
73        Self { attributes }
74    }
75}
76
77impl From<MetaList> for Attribute {
78    fn from(meta_list: MetaList) -> Self {
79        Self::Group(
80            Identifier::from(meta_list.path.segments.first().unwrap().ident.clone()),
81            Attributes {
82                attributes: meta_list
83                    .nested
84                    .into_iter()
85                    .map(|nested_meta| Attribute::from(nested_meta))
86                    .collect(),
87            },
88        )
89    }
90}
91
92impl From<Path> for Attribute {
93    fn from(path: Path) -> Self {
94        Self::Group(Identifier::from(path.segments.first().unwrap().ident.clone()), Default::default())
95    }
96}
97
98impl From<Meta> for Attribute {
99    fn from(meta: Meta) -> Self {
100        match meta {
101            syn::Meta::Path(path) => Self::from(path),
102            syn::Meta::List(list) => Self::from(list),
103            syn::Meta::NameValue(name_value) => Self::from(name_value),
104        }
105    }
106}
107
108impl From<MetaNameValue> for Attribute {
109    fn from(meta_name_value: MetaNameValue) -> Self {
110        Self::Named(
111            Identifier::from(meta_name_value.path.segments.first().unwrap().ident.clone()),
112            Literal::from(meta_name_value.lit),
113        )
114    }
115}
116
117impl From<NestedMeta> for Attribute {
118    fn from(nested_meta: NestedMeta) -> Self {
119        match nested_meta {
120            NestedMeta::Meta(meta) => Self::from(meta),
121            NestedMeta::Lit(lit) => Self::Literal(Literal::from(lit)),
122        }
123    }
124}
125
126impl ToTokens for Attributes {
127    fn to_tokens(&self, tokens: &mut TokenStream) {
128        for attribute in &self.attributes {
129            tokens.append_all(quote! { #attribute, });
130        }
131    }
132}
133
134impl ToTokens for Attribute {
135    fn to_tokens(&self, tokens: &mut TokenStream) {
136        match self {
137            Attribute::Literal(literal) => {
138                tokens.append_all(quote! {#literal})
139            }
140            Attribute::Named(_, _) => panic!("Named variant should only be used inside groups"),
141            Attribute::Group(identifier, group) => {
142                let mut attributes = TokenStream::new();
143                group
144                    .attributes
145                    .clone()
146                    .into_iter()
147                    .enumerate()
148                    .for_each(|x| {
149                        if let (index, Attribute::Named(identifier, lit)) = x {
150                            let name = Identifier::new(&identifier.name);
151                            attributes.append_all(quote! {#name = #lit});
152                            if index + 1 < group.attributes.len() {
153                                attributes.append_all(quote! {, })
154                            }
155                        } else {
156                            panic!("Group contains Non Named variant")
157                        }
158                    });
159
160                tokens.append_all(quote! {#identifier(#attributes)})
161            }
162        }
163    }
164}
165
166impl Parse for Attributes {
167    fn parse(input: ParseStream) -> syn::Result<Self> {
168        let mut metas: Vec<NestedMeta> = Vec::new();
169
170        while !input.is_empty() {
171            let value = input.parse()?;
172            metas.push(value);
173            if input.is_empty() {
174                break;
175            }
176            input.parse::<Token![,]>()?;
177        }
178        Ok(Attributes::from(metas))
179    }
180}
181
182#[cfg(test)]
183mod test {
184    use crate::ir::{Attribute, Attributes, Identifier, Literal};
185    use quote::quote;
186    use syn::{parse2, NestedMeta};
187
188    #[test]
189    fn attribute_literal() {
190        let args: NestedMeta = syn::parse_quote!("C");
191        let attr: Attribute = args.into();
192        assert_eq!(attr, Attribute::Literal(Literal::String(String::from("C"))))
193    }
194
195    #[test]
196    fn attribute_named() {
197        let args: NestedMeta = syn::parse_quote!(int = "sized");
198        let attr: Attribute = args.into();
199        assert_eq!(
200            attr,
201            Attribute::Named(
202                Identifier::new("int"),
203                Literal::String(String::from("sized"))
204            )
205        )
206    }
207
208    #[test]
209    fn attribute_group() {
210        let args: NestedMeta = syn::parse_quote!(C(int = "sized"));
211        let attr: Attribute = args.into();
212        assert_eq!(
213            attr,
214            Attribute::Group(
215                Identifier::new("C"),
216                Attributes {
217                    attributes: vec![Attribute::Named(
218                        Identifier::new("int"),
219                        Literal::String(String::from("sized"))
220                    )]
221                }
222            )
223        )
224    }
225
226    #[test]
227    fn parse_attributes() {
228        assert_eq!(
229            Attributes {
230                attributes: vec![Attribute::Group(
231                    Identifier::new("c"),
232                    Attributes {
233                        attributes: vec![Attribute::Named(
234                            Identifier::new("int"),
235                            Literal::String(String::from("sized"))
236                        )]
237                    }
238                )]
239            },
240            parse2::<Attributes>(quote! {c(int = "sized")}).expect("Failed to parse Attributes")
241        );
242    }
243}