#![allow(dead_code)]
use std::iter::Peekable;
use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
pub fn generate_compile_error(span: proc_macro::Span, message: &str) -> TokenStream {
let mut tokens = TokenStream::new();
let ident = TokenTree::Ident(Ident::new("compile_error", span));
let mut punct = TokenTree::Punct(Punct::new('!', Spacing::Alone));
punct.set_span(span);
let mut message = Literal::string(message);
message.set_span(span);
let mut message = TokenTree::Group(proc_macro::Group::new(
proc_macro::Delimiter::Parenthesis,
TokenStream::from(TokenTree::Literal(message)),
));
message.set_span(span);
let mut semi_column = Punct::new(';', Spacing::Alone);
semi_column.set_span(span);
let mut semi_column = TokenTree::Punct(semi_column);
semi_column.set_span(span);
tokens.extend(vec![ident, punct, message, semi_column]);
tokens
}
pub fn match_ident_consume<T: AsRef<str>>(
stream: &mut Peekable<impl Iterator<Item = TokenTree>>,
token: T
) -> bool {
match stream.peek() {
Some(TokenTree::Ident(ident)) if ident.to_string() == token.as_ref() => {
stream.next();
true
}
_ => false,
}
}
pub fn expect_ident_consume<T: AsRef<str>>(
stream: &mut Peekable<impl Iterator<Item = TokenTree>>,
token: T,
error: T
) -> Result<Ident, TokenStream> {
match stream.peek() {
Some(TokenTree::Ident(ident)) if ident.to_string() == token.as_ref() => {
let ident = ident.clone(); stream.next();
Ok(ident.clone())
}
Some(tt) => Err(generate_compile_error(
tt.span(),
error.as_ref()
)),
None => Err(generate_compile_error(
Span::call_site(),
error.as_ref()
)),
}
}
pub fn expect_any_ident<T: AsRef<str>>(
stream: &mut Peekable<impl Iterator<Item = TokenTree>>,
error: T
) -> Result<Ident, TokenStream> {
match stream.peek() {
Some(TokenTree::Ident(ident)) => {
let ident = ident.clone(); stream.next();
Ok(ident.clone())
}
Some(tt) => Err(generate_compile_error(
tt.span(),
error.as_ref()
)),
None => Err(generate_compile_error(
Span::call_site(),
error.as_ref()
)),
}
}
pub fn match_punct_consume<T: AsRef<str>>(
stream: &mut Peekable<impl Iterator<Item = TokenTree>>,
token: T
) -> bool {
match stream.peek() {
Some(TokenTree::Punct(punct)) if punct.as_char().to_string() == token.as_ref() => {
stream.next();
true
}
_ => false,
}
}
pub fn expect_punct_consume<T: AsRef<str>, U: AsRef<str>>(
stream: &mut Peekable<impl Iterator<Item = TokenTree>>,
token: T,
error: U
) -> Result<Punct, TokenStream> {
match stream.peek() {
Some(TokenTree::Punct(punct)) if punct.as_char().to_string() == token.as_ref() => {
let ch = punct.clone();
stream.next();
Ok(ch)
}
Some(tt) => Err(generate_compile_error(
tt.span(),
error.as_ref()
)),
None => Err(generate_compile_error(
Span::call_site(),
error.as_ref()
)),
}
}
pub fn expect_group_consume_return_inner(
stream: &mut Peekable<impl Iterator<Item = TokenTree>>,
delimiter: Delimiter,
error: &str
) -> Result<TokenStream, TokenStream> {
match stream.peek() {
Some(TokenTree::Group(group)) if group.delimiter() == delimiter => {
let inner = group.stream();
stream.next();
Ok(inner)
}
Some(tt) => Err(generate_compile_error(
tt.span(),
error
)),
None => Err(generate_compile_error(
Span::call_site(),
error
)),
}
}
pub fn expect_array_consume<T: AsRef<str>>(
tokens: &mut Peekable<impl Iterator<Item = TokenTree>>,
error: T,
) -> Result<Vec<TokenStream>, TokenStream> {
match tokens.next() {
Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Bracket => {
let mut array = Vec::new();
let mut current = TokenStream::new();
let mut inside_tokens = group.stream().into_iter();
loop {
match inside_tokens.next() {
Some(TokenTree::Punct(punct)) if punct.as_char() == ',' => {
array.push(current);
current = TokenStream::new();
}
Some(token) => current.extend(std::iter::once(token)),
None => {
array.push(current);
break;
}
}
}
Ok(array)
}
Some(tt) => Err(generate_compile_error(
tt.span(),
error.as_ref(),
)),
None => Err(generate_compile_error(
Span::call_site(),
error.as_ref(),
)),
}
}
pub fn expect_stream_before_comma_consume<T: AsRef<str>>(
tokens: &mut Peekable<impl Iterator<Item = TokenTree>>,
must_have_comma: bool,
error: T,
) -> Result<TokenStream, TokenStream> {
let mut out = TokenStream::new();
loop {
match tokens.next() {
Some(TokenTree::Punct(punct)) if punct.as_char() == ',' => {
return Ok(out);
}
Some(token) => out.extend(std::iter::once(token)),
None => {
if must_have_comma {
return Err(generate_compile_error(
Span::call_site(),
error.as_ref(),
));
}
return Ok(out);
}
}
}
}
pub fn expect_literal_consume<T: AsRef<str>>(
stream: &mut Peekable<impl Iterator<Item = TokenTree>>,
error: T
) -> Result<Literal, TokenStream> {
match stream.peek() {
Some(TokenTree::Literal(lit)) => {
let lit = lit.clone(); stream.next();
Ok(lit.clone())
}
Some(tt) => Err(generate_compile_error(
tt.span(),
error.as_ref()
)),
None => Err(generate_compile_error(
Span::call_site(),
error.as_ref()
)),
}
}
pub fn match_any_literal_consume(
stream: &mut Peekable<impl Iterator<Item = TokenTree>>,
) -> Option<Literal> {
match stream.peek() {
Some(TokenTree::Literal(lit)) => {
let lit = lit.clone(); stream.next();
Some(lit.clone())
}
_ => None,
}
}
pub fn into_peekable_iter(
tokens: TokenStream
) -> Peekable<impl Iterator<Item = TokenTree>> {
tokens.into_iter().peekable()
}
pub fn generate_doc_attribute(doc_string: &str) -> TokenStream {
let mut tokens = TokenStream::new();
let hash = TokenTree::Punct(Punct::new('#', Spacing::Alone));
let mut inner = TokenStream::new();
inner.extend(vec![
TokenTree::Ident(Ident::new("doc", Span::call_site())),
TokenTree::Punct(Punct::new('=', Spacing::Alone)),
TokenTree::Literal(Literal::string(doc_string)),
]);
let group = TokenTree::Group(Group::new(Delimiter::Bracket, inner));
tokens.extend(vec![hash, group]);
tokens
}