use std::collections::HashSet;
use std::fmt::{Debug, Formatter};
use proc_macro2::{Punct, Spacing};
use quote::{ToTokens, TokenStreamExt};
use syn::{bracketed, token, Error, ExprPath, Path, Token, Type};
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use crate::utils::pathname;
#[derive(Clone)]
pub(crate) enum ArgType {
None,
Cond(Type),
Tuple(Vec<Path>),
Permutation(Path, Path),
StrictOrder(Path, Path),
NonStrictOrder(Path, Path),
}
impl ToTokens for ArgType {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
match self {
ArgType::None => {}
ArgType::Cond(ty) => ty.to_tokens(tokens),
ArgType::Tuple(paths) => tokens.append_separated(paths, Punct::new(',', Spacing::Alone)),
ArgType::Permutation(path1, path2) => {
path1.to_tokens(tokens);
tokens.append(Punct::new('!', Spacing::Joint));
tokens.append(Punct::new('=', Spacing::Alone));
path2.to_tokens(tokens);
}
ArgType::StrictOrder(path1, path2) => {
path1.to_tokens(tokens);
tokens.append(Punct::new('!', Spacing::Joint));
tokens.append(Punct::new('<', Spacing::Alone));
path2.to_tokens(tokens);
}
ArgType::NonStrictOrder(path1, path2) => {
path1.to_tokens(tokens);
tokens.append(Punct::new('=', Spacing::Joint));
tokens.append(Punct::new('<', Spacing::Alone));
path2.to_tokens(tokens);
}
}
}
}
impl Debug for ArgType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ArgType::None => write!(f, "None"),
ArgType::Cond(c) => write!(f, "Cond({})", pathname(c)),
ArgType::Tuple(a) => write!(f, "Tuple({})", a.iter().map(pathname).collect::<Vec<_>>().join(", ")),
ArgType::Permutation(p1, p2) => write!(f, "Permutation({}, {})", pathname(p1), pathname(p2)),
ArgType::StrictOrder(p1, p2) => write!(f, "StrictOrder({}, {})", pathname(p1), pathname(p2)),
ArgType::NonStrictOrder(p1, p2) => write!(f, "NonStrictOrder({}, {})", pathname(p1), pathname(p2)),
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct TraitGen {
pub args: ArgType,
pub types: Vec<Type>,
}
#[derive(Clone)]
pub(crate) struct CondParams {
pub generic_arg: Type,
pub types: HashSet<Type>,
pub is_negated: bool
}
pub(crate) fn parse_arguments(input: ParseStream, is_conditional: bool) -> syn::parse::Result<(ArgType, Vec<Type>, bool)> {
let is_negated = is_conditional && input.peek(Token![!]) && input.parse::<Token![!]>().is_ok();
let args = if is_conditional {
ArgType::Cond(input.parse::<Type>().map_err(|e|
Error::new(e.span(), "expected substitution identifier or type")
)?)
} else {
let path1 = input.parse::<ExprPath>()?.path;
if input.peek(Token![,]) && input.parse::<Token![,]>().is_ok() {
let mut list_args = vec![path1];
loop {
let p = input.parse::<ExprPath>()?.path;
list_args.push(p);
if input.peek(Token![,]) {
_ = input.parse::<Token![,]>();
} else {
break;
}
}
ArgType::Tuple(list_args)
} else if input.peek(Token![!]) && input.parse::<Token![!]>().is_ok() {
input.parse::<Token![=]>()?;
ArgType::Permutation(path1, input.parse::<Path>()?)
} else if input.peek(Token![<]) && input.parse::<Token![<]>().is_ok() {
if input.peek(Token![=]) && input.parse::<Token![=]>().is_ok() {
ArgType::NonStrictOrder(path1, input.parse::<Path>()?)
} else {
ArgType::StrictOrder(path1, input.parse::<Path>()?)
}
} else {
ArgType::Tuple(vec![path1])
}
};
if is_conditional {
input.parse::<Token![in]>()?;
} else {
input.parse::<Token![->]>()?;
}
let types: Vec<Type>;
let vars = if is_conditional {
if input.peek(token::Bracket) {
let content;
bracketed!(content in input);
let inner_vars: ParseStream = &content;
Punctuated::<Type, Token![,]>::parse_terminated(inner_vars)?
} else {
Punctuated::<Type, Token![,]>::parse_terminated(input)?
}
} else {
Punctuated::<Type, Token![,]>::parse_terminated(input)?
};
types = vars.into_iter().collect();
if types.is_empty() {
return Err(Error::new(input.span(), format!("expected type after '{}'", if is_conditional { "in" } else { "->" })));
}
Ok((args, types, is_negated))
}
impl Parse for TraitGen {
fn parse(input: ParseStream) -> syn::parse::Result<Self> {
let (args, types, _) = parse_arguments(input, false)?;
Ok(TraitGen { args, types })
}
}
impl Parse for CondParams {
fn parse(input: ParseStream) -> syn::parse::Result<Self> {
let (args, types, is_negated) = parse_arguments(input, true)?;
let generic_arg = if let ArgType::Cond(t) = args { t } else { panic!("can't happen") };
Ok(CondParams {
generic_arg,
types: types.into_iter().collect(),
is_negated,
})
}
}