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}