|
1 | 1 | #![warn(rust_2018_idioms)]
|
2 | 2 |
|
3 |
| -use quote::{format_ident, quote, ToTokens}; |
4 |
| -use syn::punctuated::Punctuated; |
5 |
| -use syn::{parse_macro_input, DeriveInput}; |
6 |
| - |
7 |
| -#[proc_macro_derive(NSObjectProtocol, attributes(choco))] |
8 |
| -pub fn nsobjectprotocol_derive_wrapper(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
9 |
| - let input = parse_macro_input!(input as DeriveInput); |
10 |
| - nsobjectprotocol_derive(input) |
11 |
| - .unwrap_or_else(|err| err.to_compile_error()) |
12 |
| - .into() |
13 |
| -} |
14 |
| - |
15 |
| -enum ChocoAttr { |
16 |
| - Owned(syn::Ident), |
17 |
| - Framework(syn::Ident), |
18 |
| - ObjCClass(syn::Ident), |
19 |
| - Base(syn::Ident), |
20 |
| -} |
21 |
| - |
22 |
| -impl syn::parse::Parse for ChocoAttr { |
23 |
| - fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> { |
24 |
| - let attr_name: syn::Ident = input.parse()?; |
25 |
| - if attr_name == "base" { |
26 |
| - Ok(ChocoAttr::Base(attr_name)) |
27 |
| - } else if attr_name == "framework" { |
28 |
| - input.parse::<syn::Token![=]>()?; |
29 |
| - let ident: syn::Ident = input.parse()?; |
30 |
| - Ok(ChocoAttr::Framework(ident)) |
31 |
| - } else if attr_name == "objc_class" { |
32 |
| - input.parse::<syn::Token![=]>()?; |
33 |
| - let ident: syn::Ident = input.parse()?; |
34 |
| - Ok(ChocoAttr::ObjCClass(ident)) |
35 |
| - } else if attr_name == "owned" { |
36 |
| - input.parse::<syn::Token![=]>()?; |
37 |
| - let ident: syn::Ident = input.parse()?; |
38 |
| - Ok(ChocoAttr::Owned(ident)) |
39 |
| - } else { |
40 |
| - Err(input.error("unexpected attribute name")) |
41 |
| - } |
42 |
| - } |
43 |
| -} |
44 |
| - |
45 |
| -fn is_repr_transparent(meta: &syn::Meta) -> bool { |
46 |
| - if !meta.path().is_ident("repr") { |
47 |
| - return false; |
48 |
| - } |
49 |
| - |
50 |
| - let list = match meta { |
51 |
| - syn::Meta::List(list) => list, |
52 |
| - _ => return false, |
53 |
| - }; |
54 |
| - |
55 |
| - if list.nested.len() != 1 { |
56 |
| - return false; |
57 |
| - } |
58 |
| - |
59 |
| - let nested = match &list.nested[0] { |
60 |
| - syn::NestedMeta::Meta(nested) => nested, |
61 |
| - _ => return false, |
62 |
| - }; |
63 |
| - |
64 |
| - nested.path().is_ident("transparent") |
65 |
| -} |
66 |
| - |
67 |
| -fn nsobjectprotocol_derive(input: DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> { |
68 |
| - // ObjC objects should not be directly passed to C (to keep a correct refcount), |
69 |
| - // so technically #[repr(transparent)] doesn't bring much, |
70 |
| - // but at least it requires the struct to have only one non-empty field. |
71 |
| - if let Some(repr) = input.attrs.iter().find(|attr| attr.path.is_ident("repr")) { |
72 |
| - let meta = repr.parse_meta()?; |
73 |
| - if !is_repr_transparent(&meta) { |
74 |
| - return Err(syn::Error::new_spanned( |
75 |
| - repr, |
76 |
| - "#[repr(transparent)] required", |
77 |
| - )); |
78 |
| - } |
79 |
| - } else { |
80 |
| - return Err(syn::Error::new( |
81 |
| - proc_macro2::Span::call_site(), |
82 |
| - "#[repr(transparent)] required", |
83 |
| - )); |
84 |
| - } |
85 |
| - |
86 |
| - let data = match &input.data { |
87 |
| - syn::Data::Struct(struct_data) => struct_data, |
88 |
| - _ => { |
89 |
| - return Err(syn::Error::new( |
90 |
| - proc_macro2::Span::call_site(), |
91 |
| - "only structs are supported", |
92 |
| - )) |
93 |
| - } |
94 |
| - }; |
95 |
| - |
96 |
| - let struct_name = input.ident; |
97 |
| - |
98 |
| - let attrs: Punctuated<ChocoAttr, syn::Token![,]>; |
99 |
| - if let Some(choco_attr) = input.attrs.iter().find(|attr| attr.path.is_ident("choco")) { |
100 |
| - attrs = choco_attr.parse_args_with(Punctuated::parse_terminated)?; |
101 |
| - } else { |
102 |
| - attrs = Punctuated::new(); |
103 |
| - } |
104 |
| - |
105 |
| - let (owned, is_owned_different) = if let Some(owned) = |
106 |
| - attrs.iter().find_map(|attr| match attr { |
107 |
| - ChocoAttr::Owned(owned) => Some(owned), |
108 |
| - _ => None, |
109 |
| - }) { |
110 |
| - (owned, true) |
111 |
| - } else { |
112 |
| - (&struct_name, false) |
113 |
| - }; |
114 |
| - |
115 |
| - let objc_class = if let Some(objc_class) = attrs.iter().find_map(|attr| match attr { |
116 |
| - ChocoAttr::ObjCClass(objc_class) => Some(objc_class), |
117 |
| - _ => None, |
118 |
| - }) { |
119 |
| - objc_class |
120 |
| - } else { |
121 |
| - owned |
122 |
| - }; |
123 |
| - |
124 |
| - let main_field = match data.fields.iter().next() { |
125 |
| - Some(first_field) => match &first_field.ident { |
126 |
| - Some(ident) => proc_macro2::TokenTree::Ident(ident.clone()), |
127 |
| - None => proc_macro2::TokenTree::Literal(proc_macro2::Literal::u8_unsuffixed(0)), |
128 |
| - }, |
129 |
| - None => { |
130 |
| - return Err(syn::Error::new( |
131 |
| - proc_macro2::Span::call_site(), |
132 |
| - "the struct should have at least one field", |
133 |
| - )) |
134 |
| - } |
135 |
| - }; |
136 |
| - |
137 |
| - let other_fields_init: Vec<proc_macro2::TokenStream> = if is_owned_different { |
138 |
| - Vec::new() |
139 |
| - } else { |
140 |
| - data.fields |
141 |
| - .iter() |
142 |
| - .skip(1) |
143 |
| - .map(|field| { |
144 |
| - let name = field.ident.as_ref().unwrap(); |
145 |
| - quote! { |
146 |
| - #name: std::marker::PhantomData |
147 |
| - } |
148 |
| - }) |
149 |
| - .collect() |
150 |
| - }; |
151 |
| - |
152 |
| - let location = if let Some(location) = attrs.iter().find_map(|attr| match attr { |
153 |
| - ChocoAttr::Base(ident) => Some(ident), |
154 |
| - ChocoAttr::Framework(ident) => Some(ident), |
155 |
| - _ => None, |
156 |
| - }) { |
157 |
| - location |
158 |
| - } else { |
159 |
| - return Err(syn::Error::new( |
160 |
| - proc_macro2::Span::call_site(), |
161 |
| - "where the struct comes from must be specified, for example #[choco(framework = Foundation)]", |
162 |
| - )); |
163 |
| - }; |
164 |
| - |
165 |
| - let class_func = format_ident!("choco_{}_{}_class", location, objc_class); |
166 |
| - let expect_message = format!( |
167 |
| - "expecting +[{} class] to return a non null pointer", |
168 |
| - objc_class |
169 |
| - ); |
170 |
| - |
171 |
| - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); |
172 |
| - |
173 |
| - if is_owned_different { |
174 |
| - Ok(quote! { |
175 |
| - impl #impl_generics crate::base::objc::AsRawObjCPtr for #struct_name #ty_generics #where_clause { |
176 |
| - fn as_raw(&self) -> crate::base::objc::RawObjCPtr { |
177 |
| - self.#main_field |
178 |
| - } |
179 |
| - } |
180 |
| - |
181 |
| - impl #impl_generics crate::base::objc::NSObjectProtocol for #struct_name #ty_generics #where_clause { |
182 |
| - type Owned = #owned; |
183 |
| - |
184 |
| - fn class() -> crate::base::objc::ObjCClassPtr { |
185 |
| - unsafe { #class_func() } |
186 |
| - .into_opt() |
187 |
| - .expect(#expect_message) |
188 |
| - } |
189 |
| - } |
190 |
| - }) |
191 |
| - } else { |
192 |
| - Ok(quote! { |
193 |
| - impl #impl_generics crate::base::objc::AsRawObjCPtr for #struct_name #ty_generics #where_clause { |
194 |
| - fn as_raw(&self) -> crate::base::objc::RawObjCPtr { |
195 |
| - self.#main_field.as_raw() |
196 |
| - } |
197 |
| - } |
198 |
| - |
199 |
| - impl #impl_generics crate::base::objc::LikeObjCPtr for #struct_name #ty_generics #where_clause { |
200 |
| - unsafe fn from_owned_unchecked(#main_field: crate::base::objc::ObjCPtr) -> Self { |
201 |
| - Self { |
202 |
| - #main_field, |
203 |
| - #(#other_fields_init),* |
204 |
| - } |
205 |
| - } |
206 |
| - } |
207 |
| - |
208 |
| - impl #impl_generics crate::base::objc::NSObjectProtocol for #struct_name #ty_generics #where_clause { |
209 |
| - type Owned = Self; |
210 |
| - |
211 |
| - fn class() -> crate::base::objc::ObjCClassPtr { |
212 |
| - unsafe { #class_func() } |
213 |
| - .into_opt() |
214 |
| - .expect(#expect_message) |
215 |
| - } |
216 |
| - } |
217 |
| - }) |
218 |
| - } |
219 |
| -} |
220 |
| - |
221 |
| -#[proc_macro_derive(CFType)] |
222 |
| -pub fn cftype_derive_wrapper(input: proc_macro::TokenStream) -> proc_macro::TokenStream { |
223 |
| - let input = parse_macro_input!(input as DeriveInput); |
224 |
| - cftype_derive(input) |
225 |
| - .unwrap_or_else(|err| err.to_compile_error()) |
226 |
| - .into() |
227 |
| -} |
228 |
| - |
229 |
| -fn cftype_derive(input: DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> { |
230 |
| - // CFType objects should not be directly passed to C (to keep a correct refcount), |
231 |
| - // so technically #[repr(transparent)] doesn't bring much, |
232 |
| - // but at least it requires the struct to have only one non-empty field. |
233 |
| - if let Some(repr) = input.attrs.iter().find(|attr| attr.path.is_ident("repr")) { |
234 |
| - let meta = repr.parse_meta()?; |
235 |
| - if !is_repr_transparent(&meta) { |
236 |
| - return Err(syn::Error::new_spanned( |
237 |
| - repr, |
238 |
| - "#[repr(transparent)] required", |
239 |
| - )); |
240 |
| - } |
241 |
| - } else { |
242 |
| - return Err(syn::Error::new( |
243 |
| - proc_macro2::Span::call_site(), |
244 |
| - "#[repr(transparent)] required", |
245 |
| - )); |
246 |
| - } |
247 |
| - |
248 |
| - let data = match &input.data { |
249 |
| - syn::Data::Struct(struct_data) => struct_data, |
250 |
| - _ => { |
251 |
| - return Err(syn::Error::new( |
252 |
| - proc_macro2::Span::call_site(), |
253 |
| - "only structs are supported", |
254 |
| - )) |
255 |
| - } |
256 |
| - }; |
257 |
| - |
258 |
| - let struct_name = input.ident; |
259 |
| - |
260 |
| - let main_field = match data.fields.iter().next() { |
261 |
| - Some(first_field) => match &first_field.ident { |
262 |
| - Some(ident) => proc_macro2::TokenTree::Ident(ident.clone()), |
263 |
| - None => proc_macro2::TokenTree::Literal(proc_macro2::Literal::u8_unsuffixed(0)), |
264 |
| - }, |
265 |
| - None => { |
266 |
| - return Err(syn::Error::new( |
267 |
| - proc_macro2::Span::call_site(), |
268 |
| - "the struct should have at least one field", |
269 |
| - )) |
270 |
| - } |
271 |
| - }; |
272 |
| - |
273 |
| - let other_fields_init: Vec<proc_macro2::TokenStream> = data |
274 |
| - .fields |
275 |
| - .iter() |
276 |
| - .skip(1) |
277 |
| - .map(|field| { |
278 |
| - let name = field.ident.as_ref().unwrap(); |
279 |
| - quote! { |
280 |
| - #name: std::marker::PhantomData |
281 |
| - } |
282 |
| - }) |
283 |
| - .collect(); |
284 |
| - |
285 |
| - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); |
286 |
| - |
287 |
| - Ok(quote! { |
288 |
| - impl #impl_generics crate::base::objc::AsRawObjCPtr for #struct_name #ty_generics #where_clause { |
289 |
| - fn as_raw(&self) -> crate::base::objc::RawObjCPtr { |
290 |
| - self.#main_field.as_raw().into() |
291 |
| - } |
292 |
| - } |
293 |
| - |
294 |
| - impl #impl_generics crate::base::objc::LikeObjCPtr for #struct_name #ty_generics #where_clause { |
295 |
| - unsafe fn from_owned_unchecked(#main_field: crate::base::objc::ObjCPtr) -> Self { |
296 |
| - Self { |
297 |
| - #main_field: #main_field.into(), |
298 |
| - #(#other_fields_init),* |
299 |
| - } |
300 |
| - } |
301 |
| - } |
302 |
| - |
303 |
| - impl #impl_generics crate::base::core_foundation::CFTypeInterface for #struct_name #ty_generics #where_clause { |
304 |
| - fn as_raw(&self) -> crate::base::core_foundation::RawCFTypeRef { |
305 |
| - self.#main_field.as_raw() |
306 |
| - } |
307 |
| - } |
308 |
| - }) |
309 |
| -} |
| 3 | +use quote::ToTokens; |
| 4 | +use syn::parse_macro_input; |
310 | 5 |
|
311 | 6 | fn fourcc_impl(str_lit: &syn::LitStr) -> syn::Result<proc_macro2::TokenStream> {
|
312 | 7 | let text = str_lit.value();
|
|
0 commit comments