1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/// A procedural macro to create a new `GettextLanguageLoader` using
/// the current crate's `i18n.toml` configuration, and domain.
///
/// ⚠️ *This API requires the following crate features to be
/// activated: `gettext-system`.*
///
/// ## Example
///
/// ```ignore
/// use i18n_embed::gettext::{gettext_language_loader, GettextLanguageLoader};
/// let my_language_loader: GettextLanguageLoader = gettext_language_loader!();
/// ```
#[proc_macro]
#[cfg(feature = "gettext-system")]
pub fn gettext_language_loader(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let manifest = find_crate::Manifest::new().expect("Error reading Cargo.toml");
    let current_crate_package = manifest.crate_package().expect("Error reading Cargo.toml");

    // Special case for when this macro is invoked in i18n-embed tests/docs
    let i18n_embed_crate_name = if current_crate_package.name == "i18n_embed" {
        "i18n_embed".to_string()
    } else {
        manifest
            .find(|s| s == "i18n-embed")
            .expect("i18n-embed should be an active dependency in your Cargo.toml")
            .name
    };

    let i18n_embed_crate_ident =
        syn::Ident::new(&i18n_embed_crate_name, proc_macro2::Span::call_site());

    let config_file_path = i18n_config::locate_crate_paths()
        .unwrap_or_else(|error| {
            panic!(
                "gettext_language_loader!() is unable to locate i18n config file: {}",
                error
            )
        })
        .i18n_config_file;

    let config = i18n_config::I18nConfig::from_file(&config_file_path).unwrap_or_else(|err| {
        panic!(
            "gettext_language_loader!() had a problem reading i18n config file {0:?}: {1}",
            std::fs::canonicalize(&config_file_path).unwrap_or_else(|_| config_file_path.clone()),
            err
        )
    });

    if config.gettext.is_none() {
        panic!(
            "gettext_language_loader!() had a problem parsing i18n config file {0:?}: there is no `[gettext]` section",
            std::fs::canonicalize(&config_file_path).unwrap_or(config_file_path)
        )
    }

    let fallback_language = syn::LitStr::new(
        &config.fallback_language.to_string(),
        proc_macro2::Span::call_site(),
    );

    let gen = quote::quote! {
        #i18n_embed_crate_ident::gettext::GettextLanguageLoader::new(
            module_path!(),
            #fallback_language.parse().unwrap(),
        )
    };

    gen.into()
}

/// A procedural macro to create a new `FluentLanguageLoader` using
/// the current crate's `i18n.toml` configuration, and domain.
///
/// ⚠️ *This API requires the following crate features to be
/// activated: `fluent-system`.*
///
/// ## Example
///
/// ```ignore
/// use i18n_embed::fluent::{fluent_language_loader, FluentLanguageLoader};
/// let my_language_loader: FluentLanguageLoader = fluent_language_loader!();
/// ```
#[proc_macro]
#[cfg(feature = "fluent-system")]
pub fn fluent_language_loader(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let manifest = find_crate::Manifest::new().expect("Error reading Cargo.toml");
    let current_crate_package = manifest.crate_package().expect("Error reading Cargo.toml");

    // Special case for when this macro is invoked in i18n-embed tests/docs
    let i18n_embed_crate_name = if current_crate_package.name == "i18n_embed" {
        "i18n_embed".to_string()
    } else {
        manifest
            .find(|s| s == "i18n-embed")
            .expect("i18n-embed should be an active dependency in your Cargo.toml")
            .name
    };

    let i18n_embed_crate_ident =
        syn::Ident::new(&i18n_embed_crate_name, proc_macro2::Span::call_site());

    let config_file_path = i18n_config::locate_crate_paths()
        .unwrap_or_else(|error| {
            panic!(
                "fluent_language_loader!() is unable to locate i18n config file: {}",
                error
            )
        })
        .i18n_config_file;

    let config = i18n_config::I18nConfig::from_file(&config_file_path).unwrap_or_else(|err| {
        panic!(
            "fluent_language_loader!() had a problem reading i18n config file {0:?}: {1}",
            std::fs::canonicalize(&config_file_path).unwrap_or_else(|_| config_file_path.clone()),
            err
        )
    });

    if config.fluent.is_none() {
        panic!(
            "fluent_language_loader!() had a problem parsing i18n config file {0:?}: there is no `[fluent]` section",
            std::fs::canonicalize(&config_file_path).unwrap_or(config_file_path)
        )
    }

    let fallback_language = syn::LitStr::new(
        &config.fallback_language.to_string(),
        proc_macro2::Span::call_site(),
    );

    let domain_str = config
        .fluent
        .and_then(|f| f.domain)
        .unwrap_or(current_crate_package.name);
    let domain = syn::LitStr::new(&domain_str, proc_macro2::Span::call_site());

    let gen = quote::quote! {
        #i18n_embed_crate_ident::fluent::FluentLanguageLoader::new(
            #domain,
            #fallback_language.parse().unwrap(),
        )
    };

    gen.into()
}