1 个不稳定版本

0.1.0 2019年7月15日

#2133 in 过程宏

MIT/Apache

18KB
268

这是另一个类似于 quote 的Rust准引用库,它提供了 mquote! 宏,旨在提供更好的可读性和可用性。

动机

quote! 宏仅支持常规插入 #a (并且你不能在这里放入像 my_struct.field 这样的表达式)和重复插入 #(...)*

对我来说,这还不够。如果你想条件性地放入一个标记片段,你必须将它与某个变量关联,然后将其插接到 quote! 表达式

let conditional_piece = if having_fun { 
    quote!(fn funny_method() { ... }) 
} else { 
    quote!() 
};
quote!(
    #conditional_piece
    fn regular_method() { ... }
)

你不觉得这可能会很令人困惑吗?特别是如果有很多这样的东西。即使是简单的表达式 my_struct.field 也必须以这种方式处理。

引入模板 mquote!

它支持

因此,你可以重写上述代码

mquote!{
    #{if having_fun}
        fn funny_method() { ... }
    #{endif}
    fn regular_method() { ... }
}

这个crate不仅仅关于语法糖!实际上,在复杂情况下使用 mquote! 会有一些性能提升,因为它不会创建多个 TokenStream 并将它们连接起来,而是在单个 TokenStream 中处理所有内容。

该crate包含 mquote!mquote_spanned!。第一个的用法示例在下一节中。第二个允许你通过以下语法设置生成标记流的范围:mquote_spanned!(span => ...)

更多示例

表达式插入

将给定的表达式转换为标记,使用 ToTokens

fn put_filter(enabled: bool) ->  proc_macro2::TokenStream {
    let good_person = Person{ name: "Oleg", age: 20 };
    mquote!{
        assert!(!#{enabled} || person.name == #{good_person.name} 
            && person.age >= #{good_person.age})
    } 
}

如果 / elif / else

fn define_container(amount: usize) ->  proc_macro2::TokenStream {
    mquote!{
        #{if amount > 1}
            struct People(Vec<Person>);
        #{elif amount == 1}
            struct Human(Person);
        #{else}
            struct NoneHuman;
        #{endif}
    }
}

for

fn define_person(fields: Vec<(Ident, Ident)>) -> proc_macro2::TokenStream {
    mquote!{
        pub struct Person {
            #{for (name, ty) in fields}
                #{name}: #{ty}
            #{endfor}
        }
    }
}

匹配

fn hardcode_it(var: Ident, value: Option<&str>) -> proc_macro2::TokenStream {
    mquote!{
        static #var: &str = #{match value}
            #{of Some(x) if x.len() > 0}
                #{x};
            #{of Some(_)}
                "case for empty strings";
            #{of None}
                "default value";
        #{endmatch}
    }
}

扩展

有时你希望 mquote! 不克隆地消费一个 TokenTree 迭代器。可以使用特殊语法 ^{iterable} 来实现,该语法接受任何 IntoIterator<Item=TokenTree>

fn assign_by_ref(stream: TokenStream) -> TokenStream {
    let tail = stream.into_iter().drop(5); // here could be something
                                           // more reasonable
    mquote!{
        let _ = ^{tail}
    }
}

转义 #{}^{}

如果你想要直接使用 #{abc}^{abc},你应该使用双重花括号

fn it_works() {
    let tokens = mquote!(#{{abc}} ^{{abc}});
    assert_eq!(tokens.to_string(), "# { abc } ^ { abc }")
}

依赖项

~320KB