oxc_css_parser_macro/
lib.rs1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{Data, DataStruct, DeriveInput, Fields, Ident};
4
5#[proc_macro_derive(Spanned)]
6pub fn spanned_derive(input: TokenStream) -> TokenStream {
7 let ast: DeriveInput = syn::parse(input).unwrap();
8 let name = &ast.ident;
9 let generics = &ast.generics;
10
11 let generated = match &ast.data {
12 Data::Struct(..) => quote! {
13 impl #generics crate::pos::Spanned for #name #generics {
14 #[inline]
15 fn span(&self) -> &Span {
16 &self.span
17 }
18 }
19 },
20 Data::Enum(data_enum) => {
21 let variants = data_enum.variants.iter().map(|variant| {
22 let ident = &variant.ident;
23 let fields = &variant.fields;
24 match fields {
25 Fields::Unnamed(..) => quote! {
26 Self::#ident(x) => x.span(),
27 },
28 Fields::Unit => quote! {
29 Self::#ident => unimplemented!(),
30 },
31 _ => unimplemented!("enum variant with named fields is not supported"),
32 }
33 });
34 quote! {
35 impl #generics crate::pos::Spanned for #name #generics {
36 fn span(&self) -> &Span {
37 match self {
38 #(#variants)*
39 }
40 }
41 }
42 }
43 }
44 _ => unimplemented!(),
45 };
46
47 generated.into()
48}
49
50#[proc_macro_derive(SpanIgnoredEq)]
51pub fn span_ignored_eq_derive(input: TokenStream) -> TokenStream {
52 let ast: DeriveInput = syn::parse(input).unwrap();
53 let name = &ast.ident;
54 let generics = &ast.generics;
55
56 let generated = match &ast.data {
57 Data::Struct(DataStruct {
58 fields: Fields::Named(fields),
59 ..
60 }) => {
61 let comparions = fields
62 .named
63 .iter()
64 .filter_map(|field| field.ident.as_ref())
65 .filter(|ident| *ident != "span")
66 .map(|ident| quote! { && self.#ident.span_ignored_eq(&other.#ident) });
67 quote! {
68 impl #generics crate::SpanIgnoredEq for #name #generics {
69 #[must_use]
70 fn span_ignored_eq(&self, other: &Self) -> bool {
71 true #(#comparions)*
72 }
73 }
74 }
75 }
76 Data::Enum(data_enum) => {
77 let variants = data_enum.variants.iter().map(|variant| {
78 let ident = &variant.ident;
79 let fields = &variant.fields;
80 match fields {
81 Fields::Unnamed(..) => quote! {
82 (Self::#ident(a), Self::#ident(b)) => a.span_ignored_eq(&b),
83 },
84 Fields::Unit => quote! {
85 (Self::#ident, Self::#ident) => true,
86 },
87 _ => unimplemented!("enum variant with named fields is not supported"),
88 }
89 });
90 quote! {
91 impl #generics crate::SpanIgnoredEq for #name #generics {
92 #[must_use]
93 fn span_ignored_eq(&self, other: &Self) -> bool {
94 match (self, other) {
95 #(#variants)*
96 _ => false,
97 }
98 }
99 }
100 }
101 }
102 _ => unimplemented!(),
103 };
104
105 generated.into()
106}
107
108#[proc_macro_derive(EnumAsIs)]
109pub fn enum_as_is_derive(input: TokenStream) -> TokenStream {
110 use heck::ToSnakeCase;
111
112 let ast: DeriveInput = syn::parse(input).unwrap();
113 let name = &ast.ident;
114 let generics = &ast.generics;
115
116 let generated = match &ast.data {
117 Data::Enum(data_enum) => {
118 let variants = data_enum.variants.iter().map(|variant| {
119 let original_name = &variant.ident;
120 let variant_name = original_name.to_string().to_snake_case();
121
122 let ident_is = Ident::new(&format!("is_{variant_name}"), variant.ident.span());
123 let ident_as = Ident::new(&format!("as_{variant_name}"), variant.ident.span());
124
125 let doc_is = format!("Checks inner field whether it's of variant [`{original_name}`](Self::{original_name}) or not.");
126 let doc_as = format!("Returns [`Some`] with a reference to inner field if it's of variant [`{original_name}`](Self::{original_name}), otherwise returns [`None`].");
127
128 match &variant.fields {
129 Fields::Unnamed(unamed_fields) => {
130 let ty = &unamed_fields.unnamed.first().unwrap().ty;
131 quote! {
132 #[doc = #doc_is]
133 #[inline]
134 pub fn #ident_is(&self) -> bool {
135 match self {
136 Self::#original_name(..) => true,
137 _ => false
138 }
139 }
140 #[doc = #doc_as]
141 #[inline]
142 pub fn #ident_as(&self) -> Option<&#ty> {
143 match self {
144 Self::#original_name(value) => Some(value),
145 _ => None,
146 }
147 }
148 }
149 }
150 Fields::Unit => quote! {
151 #[doc = #doc_is]
152 #[inline]
153 pub fn #ident_is(&self) -> bool {
154 match self {
155 Self::#original_name => true,
156 _ => false,
157 }
158 }
159 },
160 _ => unimplemented!("enum variant with named fields is not supported"),
161 }
162 });
163
164 quote! {
165 impl #generics #name #generics {
166 #(#variants)*
167 }
168 }
169 }
170 _ => unimplemented!("only enum is supported"),
171 };
172
173 generated.into()
174}