i18n_format_macro/
lib.rs

1// SPDX-License-Identifier: MIT
2// SPDX-FileCopyrightText: (c) 2023 Hubert Figuière
3
4//! Implement the proc macros.
5//!
6
7use proc_macro::{Delimiter, Group, TokenStream, TokenTree};
8use quote::quote;
9
10#[proc_macro]
11/// Wrapper macro to use allow formatting with gettext and ngettext in
12/// a way that allow xgettext to find strings for `.po` files as it
13/// doesn't support a keyword with a `!`.
14///
15/// ```
16/// use i18n_format::i18n_fmt;
17///
18/// let number = 1;
19/// let s = i18n_fmt! {
20///     i18n_fmt("This is number {}, make it so !", number)
21/// };
22///
23/// let count = 2;
24/// let message = i18n_fmt! {
25///     i18n_nfmt("Counted {} item", "Counted {} items", count, count)
26/// };
27/// ```
28///
29/// Both `i18n_fmt` and `i18n_nfmt` are placeholders, in the block for
30/// `i18n_fmt!` either will be replaced by a call to
31/// [`i18n-format::i18n_fmt_impl`] or [`i18n-format::i18n_nfmt_impl`],
32/// respectively.  Specify `i18n_fmt` and `i18n_nfmt:1,2` as keywords
33/// for calls to xgettext.
34///
35pub fn i18n_fmt(body: TokenStream) -> TokenStream {
36    let mut macro_block: TokenStream = quote!(
37        use i18n_format::*;
38    )
39    .into();
40    macro_block.extend(body.into_iter().map(move |tt| {
41        if let TokenTree::Ident(ref i) = tt {
42            match i.to_string().as_str() {
43                "i18n_fmt" => {
44                    return TokenTree::Group(Group::new(Delimiter::None, quote!(i18n_fmt_impl!).into()))
45                }
46                "i18n_nfmt" => {
47                    return TokenTree::Group(Group::new(Delimiter::None, quote!(i18n_nfmt_impl!).into()))
48                }
49                _ => {}
50            }
51        }
52        tt
53    }));
54
55    [TokenTree::Group(Group::new(Delimiter::Brace, macro_block))]
56        .into_iter()
57        .collect()
58}