ligen_core/ir/
attribute.rs1use 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#[derive(Debug, PartialEq, Clone)]
15pub enum Attribute {
16 Literal(Literal),
18 Named(Identifier, Literal),
20 Group(Identifier, Attributes),
22}
23
24#[derive(Shrinkwrap, Default, Debug, PartialEq, Clone)]
25#[shrinkwrap(mutable)]
26pub struct Attributes {
28 pub attributes: Vec<Attribute>,
30}
31
32impl Attributes {
33 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}