2021-01-07 23:34:36 +01:00
|
|
|
extern crate proc_macro;
|
|
|
|
|
2021-04-16 15:20:25 +02:00
|
|
|
use std::iter::Peekable;
|
|
|
|
|
|
|
|
use proc_macro::token_stream::IntoIter;
|
|
|
|
use proc_macro::{TokenStream, TokenTree};
|
2021-01-07 23:34:36 +01:00
|
|
|
|
2021-01-19 21:57:48 +01:00
|
|
|
fn skip_meta(mut it: Peekable<IntoIter>) -> Peekable<IntoIter> {
|
2021-01-07 23:34:36 +01:00
|
|
|
while let Some(token) = it.peek() {
|
|
|
|
if let TokenTree::Ident(_) = token {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
it.next();
|
|
|
|
}
|
|
|
|
}
|
2021-01-19 21:57:48 +01:00
|
|
|
it
|
|
|
|
}
|
|
|
|
|
|
|
|
fn consume_ident(mut it: Peekable<IntoIter>, name: &str) -> Peekable<IntoIter> {
|
2021-01-07 23:34:36 +01:00
|
|
|
if let Some(TokenTree::Ident(ident)) = it.next() {
|
2021-01-19 21:57:48 +01:00
|
|
|
if ident.to_string().as_str() != name {
|
|
|
|
panic!("Expect to find keyword {} but was found {:?}", name, ident)
|
2021-01-07 23:34:36 +01:00
|
|
|
}
|
|
|
|
} else {
|
2021-01-19 21:57:48 +01:00
|
|
|
panic!("Expect to find keyword {} but nothing was found", name)
|
2021-01-07 23:34:36 +01:00
|
|
|
}
|
2021-01-19 21:57:48 +01:00
|
|
|
it
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(in crate) fn codegen(mut it: Peekable<IntoIter>) -> Result<String, String> {
|
2021-01-07 23:34:36 +01:00
|
|
|
let name = it
|
|
|
|
.next()
|
|
|
|
.expect("Expect to struct name but nothing was found");
|
|
|
|
|
|
|
|
let mut variants = vec![];
|
|
|
|
if let Some(TokenTree::Group(group)) = it.next() {
|
|
|
|
for token in group.stream() {
|
|
|
|
if let TokenTree::Ident(ident) = token {
|
|
|
|
variants.push(ident.to_string())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2021-01-19 21:57:48 +01:00
|
|
|
return Err("Enum variants group expected".to_string());
|
2021-01-07 23:34:36 +01:00
|
|
|
}
|
|
|
|
if variants.is_empty() {
|
2021-01-19 21:57:48 +01:00
|
|
|
return Err("Enum cannot be empty".to_string());
|
2021-01-07 23:34:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut code = format!(
|
|
|
|
r#"
|
|
|
|
pub struct {name}Iter(Option<{name}>);
|
|
|
|
|
|
|
|
impl std::iter::Iterator for {name}Iter {{
|
|
|
|
type Item = {name};
|
|
|
|
|
|
|
|
fn count(self) -> usize {{
|
|
|
|
{len}
|
|
|
|
}}
|
|
|
|
|
|
|
|
fn last(self) -> Option<Self::Item> {{
|
|
|
|
Some({name}::{last})
|
|
|
|
}}
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {{
|
|
|
|
let o = match self.0 {{
|
|
|
|
"#,
|
|
|
|
name = name,
|
|
|
|
len = variants.len(),
|
|
|
|
last = variants.last().unwrap(),
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut last_variant = "";
|
|
|
|
for (idx, variant) in variants.iter().enumerate() {
|
|
|
|
match idx {
|
|
|
|
0 => code.push_str(
|
|
|
|
format!(
|
2021-01-19 21:57:48 +01:00
|
|
|
" None => Some({name}::{variant}),\n",
|
2021-01-07 23:34:36 +01:00
|
|
|
variant = variant,
|
|
|
|
name = name
|
|
|
|
)
|
|
|
|
.as_str(),
|
|
|
|
),
|
|
|
|
_ => code.push_str(
|
|
|
|
format!(
|
2021-01-19 21:57:48 +01:00
|
|
|
" Some({name}::{last_variant}) => Some({name}::{variant}),\n",
|
2021-01-07 23:34:36 +01:00
|
|
|
last_variant = last_variant,
|
|
|
|
variant = variant,
|
|
|
|
name = name,
|
|
|
|
)
|
|
|
|
.as_str(),
|
|
|
|
),
|
|
|
|
}
|
2021-01-19 21:57:48 +01:00
|
|
|
if idx == variants.len() - 1 {
|
|
|
|
code.push_str(" _ => None,\n");
|
|
|
|
}
|
2021-01-07 23:34:36 +01:00
|
|
|
last_variant = variant.as_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
code.push_str(
|
|
|
|
format!(
|
|
|
|
r#"
|
|
|
|
}};
|
|
|
|
self.0 = o;
|
|
|
|
o
|
|
|
|
}}
|
|
|
|
}}
|
|
|
|
impl std::iter::IntoIterator for {name} {{
|
|
|
|
type Item = {name};
|
|
|
|
type IntoIter = {name}Iter;
|
|
|
|
|
|
|
|
fn into_iter(self) -> Self::IntoIter {{
|
|
|
|
{name}Iter(None)
|
|
|
|
}}
|
|
|
|
}}
|
|
|
|
"#,
|
|
|
|
name = name
|
|
|
|
)
|
|
|
|
.as_str(),
|
|
|
|
);
|
2021-01-19 21:57:48 +01:00
|
|
|
Ok(code)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[proc_macro_derive(EnumIter)]
|
|
|
|
pub fn derive_enum_iter(item: TokenStream) -> TokenStream {
|
|
|
|
let mut it = item.into_iter().peekable();
|
|
|
|
it = skip_meta(it);
|
|
|
|
it = consume_ident(it, "pub");
|
|
|
|
it = consume_ident(it, "enum");
|
|
|
|
|
|
|
|
let code = codegen(it).unwrap();
|
2021-01-07 23:34:36 +01:00
|
|
|
code.parse().unwrap()
|
|
|
|
}
|
2021-01-19 21:57:48 +01:00
|
|
|
|
|
|
|
// #[cfg(test)]
|
|
|
|
// mod tests {
|
|
|
|
// use super::codegen;
|
|
|
|
// use proc_macro::TokenStream;
|
|
|
|
// use std::str::FromStr;
|
|
|
|
//
|
|
|
|
// #[test]
|
|
|
|
// fn empty_enum() {
|
|
|
|
// let it = TokenStream::from_str("enum A {}")
|
|
|
|
// .unwrap()
|
|
|
|
// .into_iter()
|
|
|
|
// .peekable();
|
|
|
|
// let code = codegen(it);
|
|
|
|
// assert_eq!(code, Err("Enum cannot be empty".to_string()));
|
|
|
|
// }
|
|
|
|
// }
|