1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;
use {
proc_macro::TokenStream,
syn::{Attribute, Data, DeriveInput, Fields, Ident, Type},
};
#[proc_macro_derive(SocketWrapper, attributes(sink, stream))]
pub fn socket_derive(input: TokenStream) -> TokenStream {
let input: DeriveInput = syn::parse(input).unwrap();
if !only_has_inner_socket(&input.data) {
panic!("Expected to derive for struct with inner: Socket");
}
let name = input.ident;
let from_parts = quote! {
impl From<(zmq::Socket, EventedFile)> for #name {
fn from(tup: (zmq::Socket, EventedFile)) -> Self {
#name {
inner: tup.into()
}
}
}
};
let as_socket = quote! {
impl ::prelude::AsSocket for #name {
fn socket(self) -> Socket {
self.inner
}
}
};
let stream = if has_attr(&input.attrs, "stream") {
quote!{
impl ::prelude::StreamSocket for #name {}
}
} else {
quote!{}
};
let sink = if has_attr(&input.attrs, "sink") {
quote!{
impl ::prelude::SinkSocket for #name {}
}
} else {
quote!{}
};
let full = quote! {
#from_parts
#as_socket
#stream
#sink
};
full.into()
}
fn has_attr(attrs: &[Attribute], name: &str) -> bool {
attrs.iter().any(|attr| {
attr.path
.segments
.last()
.map(|seg| seg.into_value())
.map(|seg| seg.ident == Ident::new(name, seg.ident.span()))
.unwrap_or(false)
})
}
fn only_has_inner_socket(input: &Data) -> bool {
let data_struct = match *input {
Data::Struct(ref data_struct) => data_struct,
_ => return false,
};
let fields_named = match data_struct.fields {
Fields::Named(ref fields_named) => fields_named,
_ => return false,
};
if fields_named.named.len() != 1 {
return false;
}
let field = fields_named.named.first().unwrap().into_value();
let found = field
.ident
.as_ref()
.map(|id| *id == Ident::new("inner", id.span()))
.unwrap_or(false);
if !found {
return false;
}
let type_path = match field.ty {
Type::Path(ref type_path) => type_path,
_ => return false,
};
type_path
.path
.segments
.last()
.map(|ps| ps.into_value())
.map(|ps| ps.ident == Ident::new("Socket", ps.ident.span()))
.unwrap_or(false)
}