Skip to content

Commit

Permalink
Rewrite without synstructure
Browse files Browse the repository at this point in the history
  • Loading branch information
Riley Trautman committed Apr 6, 2018
1 parent bbf4b84 commit 6606449
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 81 deletions.
8 changes: 4 additions & 4 deletions tokio-zmq-derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
[package]
name = "tokio-zmq-derive"
description = "Provides derivation for Tokio ZMQ Socket wrapper types"
version = "0.4.1"
version = "0.4.2"
license = "GPL-3.0"
authors = ["Riley Trautman <[email protected]>"]
repository = "https://github.com/asonix/tokio-zmq"

[dependencies]
quote = "0.3"
syn = "0.11"
synstructure = "0.6"
quote = "0.5"
syn = "0.13"
proc-macro2 = "0.3"

[lib]
proc-macro = true
207 changes: 130 additions & 77 deletions tokio-zmq-derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,108 +1,161 @@
extern crate proc_macro;
extern crate proc_macro2;
extern crate syn;

#[macro_use]
extern crate quote;
#[macro_use]
extern crate synstructure;

decl_derive!([SocketWrapper, attributes(stream, sink, try_from)] => socket_derive);
use proc_macro::TokenStream;
use proc_macro2::TokenTree;
use syn::{Attribute, Data, DeriveInput, Fields, Ident, Type};

#[proc_macro_derive(SocketWrapper, attributes(sink, stream, try_from))]
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;

fn socket_derive(s: synstructure::Structure) -> quote::Tokens {
let socket_binding = s.variants().iter().find(|v| {
v.bindings().iter().any(|b| {
if let Some(ref ident) = b.ast().ident {
if ident == "inner" {
return true;
let from_parts = quote! {
impl From<(zmq::Socket, PollEvented<File<ZmqFile>>)> for #name {
fn from(tup: (zmq::Socket, PollEvented<File<ZmqFile>>)) -> Self {
#name {
inner: tup.into()
}
}
}
};

false
})
});

if let Some(sb) = socket_binding {
let name = sb.ast().ident;
let from_parts = s.bound_impl(
"From<(zmq::Socket, PollEvented<File<ZmqFile>>)>",
quote! {
fn from(tup: (zmq::Socket, PollEvented<File<ZmqFile>>)) -> Self {
#name {
inner: tup.into(),
let as_socket = quote! {
impl ::prelude::AsSocket for #name {
fn socket(self) -> Socket {
self.inner
}
}
};

let try_from = {
let socket_type = Ident::from(format!("{}", name).to_uppercase().as_ref());

let try_from_attr = input.attrs.iter().find(|attr| {
attr.path
.segments
.last()
.map(|seg| seg.into_value())
.map(|seg| seg.ident == Ident::new("try_from", seg.ident.span()))
.unwrap_or(false)
});

let try_from_config = if let Some(attr) = try_from_attr {
attr.tts
.clone()
.into_iter()
.filter_map(|token_tree| match token_tree {
TokenTree::Literal(literal) => {
Some(Ident::from(format!("{}", literal).trim_matches('"')))
}
}
},
);
let as_socket = s.bound_impl(
"::prelude::AsSocket",
quote! {
fn socket(self) -> Socket {
self.inner
}
},
);

let try_from = build_try_from(&s, name);

let stream = if has_attr(&s, "stream") {
s.bound_impl("::prelude::StreamSocket", quote!{})
_ => None,
})
.next()
} else {
quote!{}
Some(Ident::from("SockConfig"))
};

let sink = if has_attr(&s, "sink") {
s.bound_impl("::prelude::SinkSocket", quote!{})
} else {
quote!{}
let conf = match try_from_config {
Some(conf) => conf,
None => panic!("try_from must take the name of the config struct"),
};

quote! {
#from_parts
#as_socket
#stream
#sink
#try_from
quote!{
impl<'a> TryFrom<#conf<'a>> for #name {
type Error = Error;

fn try_from(conf: #conf<'a>) -> Result<Self, Self::Error> {
Ok(#name {
inner: conf.build(zmq::#socket_type)?,
})
}
}
}
};

let stream = if has_attr(&input.attrs, "stream") {
quote!{
impl ::prelude::StreamSocket for #name {}
}
} else {
panic!("Could not find socket");
}
}
quote!{}
};

fn has_attr(s: &synstructure::Structure, attr: &str) -> bool {
s.ast().attrs.iter().any(|a| is_attr(a, attr))
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
#try_from
};

full.into()
}

fn is_attr(a: &syn::Attribute, attr: &str) -> bool {
a.name() == attr
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 build_try_from(s: &synstructure::Structure, name: &syn::Ident) -> quote::Tokens {
let socket_type = syn::Ident::new(format!("{}", name).to_uppercase());
fn only_has_inner_socket(input: &Data) -> bool {
let data_struct = match *input {
Data::Struct(ref data_struct) => data_struct,
_ => return false, // TODO: Make this work for enums with sockets in each varient?
};

let try_from_attr = s.ast().attrs.iter().find(|a| is_attr(a, "try_from"));
let fields_named = match data_struct.fields {
Fields::Named(ref fields_named) => fields_named,
_ => return false, // TODO: Allow other kinds of structs?
};

let mut try_from_conf = None;

if let Some(ref try_from) = try_from_attr {
if let syn::MetaItem::NameValue(_, syn::Lit::Str(ref name, _)) = try_from.value {
let name: String = name.to_owned();
try_from_conf = Some(syn::Ident::new(name));
} else {
panic!("try_from must take the name of the config struct");
}
if fields_named.named.len() != 1 {
return false;
}

let conf = try_from_conf.unwrap_or(syn::Ident::new("SockConfig"));
let field = fields_named.named.first().unwrap().into_value();

quote! {
impl<'a> TryFrom<#conf<'a>> for #name {
type Error = Error;
let found = field
.ident
.map(|id| id == Ident::new("inner", id.span()))
.unwrap_or(false);

fn try_from(conf: #conf<'a>) -> Result<Self, Self::Error> {
Ok(#name {
inner: conf.build(zmq::#socket_type)?,
})
}
}
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)
}

0 comments on commit 6606449

Please sign in to comment.