Skip to content

Commit efb6a51

Browse files
Reworking of the inner workings
Not relying on macros anymore (the only macro left is fourcc) CF not working yet
1 parent 13839a5 commit efb6a51

File tree

13 files changed

+1294
-1348
lines changed

13 files changed

+1294
-1348
lines changed

macro/src/lib.rs

Lines changed: 2 additions & 307 deletions
Original file line numberDiff line numberDiff line change
@@ -1,312 +1,7 @@
11
#![warn(rust_2018_idioms)]
22

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;
3105

3116
fn fourcc_impl(str_lit: &syn::LitStr) -> syn::Result<proc_macro2::TokenStream> {
3127
let text = str_lit.value();

0 commit comments

Comments
 (0)