use proc_macro_error::abort;
use syn::{
parenthesized,
parse::{Parse, ParseStream},
parse2,
punctuated::Punctuated,
Ident, Result, Token,
};
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct CallExpr {
pub(crate) method: Ident,
pub(crate) args: Vec<Ident>,
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) enum Arg {
CallExpr(CallExpr),
}
impl Parse for Arg {
fn parse(stream: ParseStream<'_>) -> Result<Self> {
let method = stream.parse()?;
let content;
parenthesized!(content in stream);
let args: Vec<Ident> = Punctuated::<Ident, Token![,]>::parse_separated_nonempty(&content)?
.into_iter()
.collect();
Ok(Self::CallExpr(CallExpr { method, args }))
}
}
#[derive(Debug, Default, PartialEq, Eq)]
pub(crate) struct SkipIfArgs {
pub(crate) elements: Vec<Arg>,
}
impl Parse for SkipIfArgs {
fn parse(stream: ParseStream<'_>) -> Result<Self> {
let elements: Vec<Arg> = Punctuated::<Arg, Token![,]>::parse_separated_nonempty(stream)?
.into_iter()
.collect();
Ok(Self { elements })
}
}
impl SkipIfArgs {
pub(crate) fn parse(ts: proc_macro2::TokenStream) -> Self {
match parse2::<Self>(ts) {
Ok(args) => args,
Err(e) => {
abort!(e.span(), e)
}
}
}
}
#[cfg(test)]
mod test {
use proc_macro2::TokenStream;
use quote::quote;
use rstest::rstest;
use super::*;
use crate::test::*;
#[rstest]
#[case::single_call_arg(
quote!(missing(ENV_VAR)),
skip_if_args(vec![call_expr("missing", vec!["ENV_VAR"])]),
)]
#[case::multiple_call_args(
quote!(missing(ENV_VAR1, ENV_VAR2, ENV_VAR3)),
skip_if_args(vec![call_expr(
"missing",
vec!["ENV_VAR1", "ENV_VAR2", "ENV_VAR3"],
)]),
)]
#[case::arbitrary_call_iden(
quote!(meow(ARG), docker_container(NAME)),
skip_if_args(vec![
call_expr("meow", vec!["ARG"]),
call_expr("docker_container", vec!["NAME"]),
]),
)]
#[case::multiple_instances_of_same_arg(
quote!(meow(ARG), meow(ARG)),
skip_if_args(vec![
call_expr("meow", vec!["ARG"]),
call_expr("meow", vec!["ARG"]),
]),
)]
fn parse_skipif_args(#[case] ts: TokenStream, #[case] expected: SkipIfArgs) -> Result<()> {
let parsed = parse2::<SkipIfArgs>(ts)?;
assert_eq!(expected, parsed);
Ok(())
}
}