skipif 0.1.0

Turn test cases into no-ops with _SKIPPED appended to their name based on compile time conditions.
Documentation
// Copyright 2024 Steve Wayne Warren
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use proc_macro::TokenStream;
use syn::{parse_macro_input, ItemFn};

mod analyze;
mod codegen;
mod parse;

#[proc_macro_attribute]
pub fn skip_if(args: TokenStream, input: TokenStream) -> TokenStream {
    let item = parse_macro_input!(input as ItemFn);
    let args = parse::SkipIfArgs::parse(args.into());
    let skipif = analyze::SkipIf::analyze(args);

    skipif.codegen(item).into()
}

#[cfg(test)]
mod test {
    use proc_macro2::Span;
    use syn::Ident;

    use assert_cmd as _;
    use predicates as _;

    use super::analyze::RequiredElement;
    use super::analyze::SkipIf;
    use super::parse::Arg;
    use super::parse::CallExpr;
    use super::parse::SkipIfArgs;

    pub(crate) fn required_env_vars(ss: Vec<&str>) -> Vec<RequiredElement> {
        ss.into_iter()
            .map(|s| RequiredElement::EnvVar(s.to_string()))
            .collect()
    }

    pub(crate) fn skipif(elements: Vec<Vec<RequiredElement>>) -> SkipIf {
        SkipIf {
            required_elements: elements
                .into_iter()
                .map(IntoIterator::into_iter)
                .flatten()
                .collect(),
        }
    }

    pub(crate) fn call_expr(s: &str, ss: Vec<&str>) -> Arg {
        let args = ss
            .into_iter()
            .map(|s| Ident::new(s, Span::mixed_site()))
            .collect();

        Arg::CallExpr(CallExpr {
            method: Ident::new(s, Span::mixed_site()),
            args,
        })
    }

    pub(crate) fn skip_if_args(elements: Vec<Arg>) -> SkipIfArgs {
        SkipIfArgs { elements }
    }
}