xmacro_lib 0.5.1

macro engine for producing multiple expansions
Documentation
#![doc = include_str!("../README.md")]

#[allow(clippy::wildcard_imports)]
use unsynn::*;

mod expand;
mod xmacro;
mod xtoken;

use crate::xmacro::{XMacro, XMacroItem};

/// Does general expansion of x-macro definitions.
///
/// # Errors
///
/// Returns an `unsynn::Error` if the input did not parse.
///
/// # Example
///
/// ```
/// # use unsynn::ToTokens;
/// let token_stream = "$((a)(b)) $((c)(d))".to_token_stream();
///
/// let expanded = xmacro_lib::xmacro_expand(token_stream).unwrap();
/// assert_eq!(expanded.to_string(), "a c a d b c b d".tokens_to_string());
/// ```
pub fn xmacro_expand(input: TokenStream) -> Result<TokenStream> {
    let xtokens = XMacro::parse_all(&mut input.into_token_iter())?.expand();
    let mut output = TokenStream::new();
    output.extend(xtokens.into_iter().map(|xtoken| xtoken.to_token_stream()));
    Ok(output)
}

/// Does per item expansion of x-macro definitions.
/// Unlike the general expansion above this function expands each 'item' as if when it is in
/// separate scope.
///
/// # Errors
///
/// Returns an `unsynn::Error` if the input did not parse.
///
/// # Example
///
/// ```
/// # use unsynn::ToTokens;
/// let token_stream = r#"
/// struct $(A B){}
/// struct $(C D);
/// "#.to_token_stream();
///
/// let expanded = xmacro_lib::xmacro_expand_items(token_stream)
///                    .unwrap()
///                    .tokens_to_string();
///
/// let expected = r#"
/// struct A{}
/// struct B{}
/// struct C;
/// struct D;
/// "#.tokens_to_string();
///
/// assert_eq!(expanded, expected);
/// ```
pub fn xmacro_expand_items(input: TokenStream) -> Result<TokenStream> {
    let mut input = input.into_token_iter();
    let mut output = TokenStream::new();

    while Except::<EndOfStream>::parse(&mut input).is_ok() {
        let xtokens = XMacroItem::parse(&mut input)?.0.expand();
        output.extend(xtokens.map(|xtoken| xtoken.to_token_stream()));
    }
    Ok(output)
}

// helper macros for tests
#[doc(hidden)] // TODO: figure out how to define this only for doc and test builds
#[macro_export]
macro_rules! xpand {
    ($($in:tt)*) => {
        xmacro_lib::xmacro_expand(stringify!{ $($in)* }.to_token_stream()).unwrap().tokens_to_string()
    }
}

#[doc(hidden)] // TODO: figure out how to define this only for doc and test builds
#[macro_export]
macro_rules! xpect {
    ($($out:tt)*) => {
        stringify!{ $($out)* }.tokens_to_string()
    }
}

#[doc(hidden)] // TODO: figure out how to define this only for doc and test builds
#[macro_export]
macro_rules! CHECK_EXPAND {
    ($($explanation:literal)? {$($in:tt)*} == {$($out:tt)*}) => {
        {
            use unsynn::ToTokens;
            use xmacro_lib::*;
            assert_eq!(
                xpand!($($in)*),
                xpect!($($out)*)
                $(, $explanation)?
            );
        };
    };
}

// helper macro for (doc-) tests
// helper when generating docs to prevent partial snippets flagged as compile errors.
#[doc(hidden)] // TODO: figure out how to define this only for doc and test builds
#[macro_export]
macro_rules! doctest_ignore {
    ($($tt:tt)*) => {};
}