-
-
Save DanielKeep/4bfb8a947f8e3f196bdd to your computer and use it in GitHub Desktop.
custom_derive!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
Provides a very bare-bones way of having custom `derive` support in stable Rust. | |
*/ | |
// This example requires nightly for rustc_serialize because I don't want to involve Cargo. | |
#![feature(rustc_private)] | |
extern crate serialize as rustc_serialize; | |
// #![feature(trace_macros)] | |
macro_rules! as_item { | |
($($i:item)*) => {$($i)*}; | |
} | |
macro_rules! custom_derive { | |
( | |
$(#[$($attrs:tt)*])* | |
enum $($it:tt)* | |
) => { | |
custom_derive! { | |
@split_attrs | |
($(#[$($attrs)*],)*), (), (), | |
(enum $($it)*) | |
} | |
}; | |
( | |
$(#[$($attrs:tt)*])* | |
pub $($it:tt)* | |
) => { | |
custom_derive! { | |
@split_attrs | |
($(#[$($attrs)*],)*), (), (), | |
(pub $($it)*) | |
} | |
}; | |
( | |
$(#[$($attrs:tt)*])* | |
struct $($it:tt)* | |
) => { | |
custom_derive! { | |
@split_attrs | |
($(#[$($attrs)*],)*), (), (), | |
(struct $($it)*) | |
} | |
}; | |
( | |
@split_attrs | |
(), | |
$non_derives:tt, | |
$derives:tt, | |
$it:tt | |
) => { | |
custom_derive! { | |
@split_derive_attrs | |
{ $non_derives, $it }, | |
$derives, | |
(), | |
() | |
} | |
}; | |
( | |
@split_attrs | |
(#[derive($($new_drv:ident),*)], $(#[$($attrs:tt)*],)*), | |
$non_derives:tt, | |
($($derives:meta,)*), | |
$it:tt | |
) => { | |
custom_derive! { | |
@split_attrs | |
($(#[$($attrs)*],)*), | |
$non_derives, | |
($($derives,)* $(#[$new_drv],)*), | |
$it | |
} | |
}; | |
( | |
@split_attrs | |
(#[$new_attr:meta], $(#[$($attrs:tt)*],)*), | |
($($non_derives:tt)*), | |
$derives:tt, | |
$it:tt | |
) => { | |
custom_derive! { | |
@split_attrs | |
($(#[$($attrs)*],)*), | |
($($non_derives)* #[$new_attr],), | |
$derives, | |
$it | |
} | |
}; | |
/* | |
Built-in derives: | |
Clone, Hash, RustcEncodable, RustcDecodable, PartialEq, Eq, PartialOrd, Ord, | |
Debug, Default, Send, Sync, Copy, | |
*/ | |
(@split_derive_attrs | |
{ ($(#[$($non_derives:tt)*],)*), ($($it:tt)*) }, | |
(), ($($bi_drvs:ident,)*), ($($user_drvs:ident,)*) | |
) => { | |
as_item! { | |
#[derive($($bi_drvs,)*)] | |
$(#[$($non_derives)*])* | |
$($it)* | |
} | |
custom_derive! { | |
@expand_user_drvs | |
($($user_drvs,)*), ($($it)*) | |
} | |
}; | |
(@split_derive_attrs | |
$fixed:tt, | |
(#[Clone], $($tail:tt)*), ($($bi_drvs:meta,)*), $user_drvs:tt | |
) => { | |
custom_derive! { | |
@split_derive_attrs | |
$fixed, | |
($($tail)*), ($($bi_drvs,)* Clone,), $user_drvs | |
} | |
}; | |
(@split_derive_attrs | |
$fixed:tt, | |
(#[RustcEncodable], $($tail:tt)*), ($($bi_drvs:meta,)*), $user_drvs:tt | |
) => { | |
custom_derive! { | |
@split_derive_attrs | |
$fixed, | |
($($tail)*), ($($bi_drvs,)* RustcEncodable,), $user_drvs | |
} | |
}; | |
(@split_derive_attrs | |
$fixed:tt, | |
(#[RustcDecodable], $($tail:tt)*), ($($bi_drvs:meta,)*), $user_drvs:tt | |
) => { | |
custom_derive! { | |
@split_derive_attrs | |
$fixed, | |
($($tail)*), ($($bi_drvs,)* RustcDecodable,), $user_drvs | |
} | |
}; | |
(@split_derive_attrs | |
$fixed:tt, | |
(#[PartialEq], $($tail:tt)*), ($($bi_drvs:meta,)*), $user_drvs:tt | |
) => { | |
custom_derive! { | |
@split_derive_attrs | |
$fixed, | |
($($tail)*), ($($bi_drvs,)* PartialEq,), $user_drvs | |
} | |
}; | |
(@split_derive_attrs | |
$fixed:tt, | |
(#[Eq], $($tail:tt)*), ($($bi_drvs:meta,)*), $user_drvs:tt | |
) => { | |
custom_derive! { | |
@split_derive_attrs | |
$fixed, | |
($($tail)*), ($($bi_drvs,)* Eq,), $user_drvs | |
} | |
}; | |
(@split_derive_attrs | |
$fixed:tt, | |
(#[PartialOrd], $($tail:tt)*), ($($bi_drvs:meta,)*), $user_drvs:tt | |
) => { | |
custom_derive! { | |
@split_derive_attrs | |
$fixed, | |
($($tail)*), ($($bi_drvs,)* PartialOrd,), $user_drvs | |
} | |
}; | |
(@split_derive_attrs | |
$fixed:tt, | |
(#[Ord], $($tail:tt)*), ($($bi_drvs:meta,)*), $user_drvs:tt | |
) => { | |
custom_derive! { | |
@split_derive_attrs | |
$fixed, | |
($($tail)*), ($($bi_drvs,)* Ord,), $user_drvs | |
} | |
}; | |
(@split_derive_attrs | |
$fixed:tt, | |
(#[Debug], $($tail:tt)*), ($($bi_drvs:meta,)*), $user_drvs:tt | |
) => { | |
custom_derive! { | |
@split_derive_attrs | |
$fixed, | |
($($tail)*), ($($bi_drvs,)* Debug,), $user_drvs | |
} | |
}; | |
(@split_derive_attrs | |
$fixed:tt, | |
(#[Default], $($tail:tt)*), ($($bi_drvs:meta,)*), $user_drvs:tt | |
) => { | |
custom_derive! { | |
@split_derive_attrs | |
$fixed, | |
($($tail)*), ($($bi_drvs,)* Default,), $user_drvs | |
} | |
}; | |
(@split_derive_attrs | |
$fixed:tt, | |
(#[Send], $($tail:tt)*), ($($bi_drvs:meta,)*), $user_drvs:tt | |
) => { | |
custom_derive! { | |
@split_derive_attrs | |
$fixed, | |
($($tail)*), ($($bi_drvs,)* Send,), $user_drvs | |
} | |
}; | |
(@split_derive_attrs | |
$fixed:tt, | |
(#[Sync], $($tail:tt)*), ($($bi_drvs:meta,)*), $user_drvs:tt | |
) => { | |
custom_derive! { | |
@split_derive_attrs | |
$fixed, | |
($($tail)*), ($($bi_drvs,)* Sync,), $user_drvs | |
} | |
}; | |
(@split_derive_attrs | |
$fixed:tt, | |
(#[Copy], $($tail:tt)*), ($($bi_drvs:meta,)*), $user_drvs:tt | |
) => { | |
custom_derive! { | |
@split_derive_attrs | |
$fixed, | |
($($tail)*), ($($bi_drvs,)* Copy,), $user_drvs | |
} | |
}; | |
(@split_derive_attrs | |
$fixed:tt, | |
(#[$($new_user:tt)*], $($tail:tt)*), $bi_drvs:tt, ($($user_drvs:meta,)*) | |
) => { | |
custom_derive! { | |
@split_derive_attrs | |
$fixed, ($($tail)*), $bi_drvs, ($($user_drvs,)* $($new_user)*,) | |
} | |
}; | |
(@expand_user_drvs | |
(), ($($it:tt)*) | |
) => {}; | |
(@expand_user_drvs | |
($user_drv:ident, $($tail:ident,)*), ($($it:tt)*) | |
) => { | |
$user_drv! { $($it)* } | |
custom_derive! { | |
@expand_user_drvs | |
($($tail)*), ($($it)*) | |
} | |
}; | |
} | |
// trace_macros!(true); | |
macro_rules! EnumIterator { | |
($(pub)* enum $name:ident { $($body:tt)* }) => { | |
EnumIterator! { | |
@collect_variants ($name), | |
($($body)*,) -> () | |
} | |
}; | |
( | |
@collect_variants ($name:ident), | |
($(,)*) -> ($($var_names:ident,)*) | |
) => { | |
type NameIter = ::std::vec::IntoIter<&'static str>; | |
type VariantIter = ::std::vec::IntoIter<$name>; | |
impl $name { | |
#[allow(dead_code)] | |
pub fn iter_variants() -> VariantIter { | |
vec![$($name::$var_names),*].into_iter() | |
} | |
#[allow(dead_code)] | |
pub fn iter_variant_names() -> NameIter { | |
vec![$(stringify!($var_names)),*].into_iter() | |
} | |
} | |
}; | |
( | |
@collect_variants $fixed:tt, | |
($var:ident $(= $_val:expr)*, $($tail:tt)*) -> ($($var_names:tt)*) | |
) => { | |
EnumIterator! { | |
@collect_variants $fixed, | |
($($tail)*) -> ($($var_names)* $var,) | |
} | |
}; | |
( | |
@collect_variants ($name:ident), | |
($var:ident $_struct:tt, $($tail:tt)*) -> ($($var_names:tt)*) | |
) => { | |
const _error: () = concat!( | |
"cannot derive EnumIterator for ", | |
stringify!($name), | |
", due to non-unitary variant ", | |
stringify!($var), | |
"." | |
); | |
}; | |
} | |
macro_rules! StableEncodable { | |
( | |
$(pub)* struct $name:ident { $($body:tt)* } | |
) => { | |
StableEncodable! { | |
@impl $name, | |
bounds(), | |
ty_clss(), | |
{ $($body)* } | |
} | |
}; | |
( | |
$(pub)* struct $name:ident < $($tail:tt)* | |
) => { | |
StableEncodable! { | |
@extract_gen_args $name, | |
($($tail)*) | |
-> bounds(), ty_clss(where) | |
} | |
}; | |
( | |
@impl $name:ident, | |
bounds($($bounds:tt)*), | |
ty_clss($($ty_clss:tt)*), | |
{ $($fnames:ident: $_ftys:ty),* $(,)* } | |
) => { | |
StableEncodable! { | |
@as_item | |
impl<$($bounds)*> rustc_serialize::Encodable for $name<$($bounds)*> | |
$($ty_clss)* { | |
fn encode<S>(&self, s: &mut S) -> Result<(), S::Error> | |
where S: rustc_serialize::Encoder { | |
const NUM_FIELDS: usize = StableEncodable!(@count_tts $($fnames)*); | |
try!(s.emit_struct(stringify!($name), NUM_FIELDS, |s| { | |
// Poor man's enumerate!($($fnames)): | |
let mut idx = 0; | |
$( | |
try!(s.emit_struct_field(stringify!($fnames), idx, |s| { | |
self.$fnames.encode(s) | |
})); | |
idx += 1; | |
)* | |
let _ = idx; | |
Ok(()) | |
})); | |
Ok(()) | |
} | |
} | |
} | |
}; | |
(@as_item $i:item) => {$i}; | |
( | |
@extract_gen_args $name:ident, | |
(> { $($tail:tt)* }) | |
-> bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*) | |
) => { | |
StableEncodable! { | |
@impl $name, | |
bounds($($bounds)*), | |
ty_clss($($ty_clss)*), | |
{ $($tail)* } | |
} | |
}; | |
( | |
@extract_gen_args $name:ident, | |
($ty_name:ident: $($tail)*) | |
-> bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*) | |
) => { | |
StableEncodable! { | |
@skip_inline_bound $name, | |
($($tail)*) | |
-> bounds($($bounds)* $ty_name:), | |
ty_clss($($ty_clss)* $ty_name: ::rustc_serialize::Encodable,) | |
} | |
}; | |
( | |
@extract_gen_args $name:ident, | |
($ty_name:ident $($tail:tt)*) | |
-> bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*) | |
) => { | |
StableEncodable! { | |
@extract_gen_args $name, | |
($($tail)*) | |
-> bounds($($bounds)* $ty_name), | |
ty_clss($($ty_clss)* $ty_name: ::rustc_serialize::Encodable,) | |
} | |
}; | |
( | |
@extract_gen_args $name:ident, | |
(, $($tail:tt)*) | |
-> bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*) | |
) => { | |
StableEncodable! { | |
@extract_gen_args $name, | |
($($tail)*) | |
-> bounds($($bounds)* ,), ty_clss($($ty_clss)*) | |
} | |
}; | |
( | |
@extract_gen_args $name:ident, | |
($lt:tt $($tail:tt)*) | |
-> bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*) | |
) => { | |
StableEncodable! { | |
@extract_gen_args $name, | |
($($tail)*) | |
-> bounds($($bounds)* $lt), ty_clss($($ty_clss)*) | |
} | |
}; | |
( | |
@skip_inline_bound $name:ident, | |
(, $($tail:tt)*) | |
-> bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*) | |
) => { | |
StableEncodable! { | |
@extract_gen_args $name, | |
($($tail)*) | |
-> bounds($($bounds)* ,), ty_clss($($ty_clss)*) | |
} | |
}; | |
( | |
@skip_inline_bound $name:ident, | |
(> { $($tail:tt)* }) | |
-> bounds($($bounds:tt)*), ty_clss($($ty_clss:tt)*) | |
) => { | |
StableEncodable! { | |
@impl $name, | |
bounds($($bounds)*), | |
ty_clss($($ty_clss)*), | |
{ $($tail)* } | |
} | |
}; | |
(@count_tts) => {0usize}; | |
(@count_tts $_tt:tt $($tail:tt)*) => {1usize + StableEncodable!(@count_tts $($tail)*)}; | |
} | |
custom_derive! { | |
#[derive(Debug, EnumIterator)] | |
enum Get { Up, Down, AllAround } | |
} | |
custom_derive! { | |
#[derive(Debug, StableEncodable)] | |
struct LazyEg<A> { a: A, b: i32, c: (u8, u8, u8) } | |
} | |
fn main() { | |
for (name, var) in Get::iter_variant_names().zip(Get::iter_variants()) { | |
println!("{:?}: {:?}", name, var) | |
} | |
let lazy_eg = LazyEg { | |
a: String::from("Muahahaha and you probably thought it couldn't be done!"), | |
b: 42, | |
c: (1, 3, 0), | |
}; | |
println!("lazy_eg: {}", rustc_serialize::json::encode(&lazy_eg).unwrap()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment