typed_i18n_derive/
lib.rs

1#![forbid(unsafe_code)]
2#![forbid(missing_docs)]
3#![warn(clippy::pedantic)]
4
5//! Derive macro for [typed-i18n](https://docs.rs/typed-i18n/latest/typed-i18n/).
6
7use proc_macro::TokenStream;
8use proc_macro_error::{abort, proc_macro_error};
9use std::env;
10use std::path::PathBuf;
11use syn::spanned::Spanned;
12use syn::{parse_macro_input, DeriveInput};
13use typed_i18n_support::attribute::Attributes;
14use typed_i18n_support::diagnostic::ProcMacroError;
15use typed_i18n_support::languages::RawLanguages;
16use typed_i18n_support::messages::Messages;
17
18/// Macro to convert a language file and an enum into a type safe i18n system.
19#[proc_macro_error]
20#[proc_macro_derive(TypedI18N, attributes(typed_i18n))]
21pub fn typed_i18n(item: TokenStream) -> TokenStream {
22    let diagnostic = &mut ProcMacroError;
23
24    let input = parse_macro_input!(item as DeriveInput);
25    let span = input.span();
26    let with_mixed_str = cfg!(feature = "alloc");
27    let attributes = Attributes::parse(diagnostic, span, with_mixed_str, input.attrs);
28
29    let file_path = {
30        let project_root = env::var("CARGO_MANIFEST_DIR_OVERRIDE")
31            .or(env::var("CARGO_MANIFEST_DIR"))
32            .unwrap_or_else(|_| ".".into());
33        let mut path = PathBuf::from(project_root);
34        path.push(&attributes.parameters.filename);
35        path
36    };
37
38    let contents = std::fs::read_to_string(&file_path)
39        .unwrap_or_else(|e| abort!(span, format!("Error reading file {file_path:?}: {e}")));
40
41    let languages = RawLanguages::parse(diagnostic, span, input.data);
42
43    let languages = languages.into(diagnostic, span);
44
45    let messages = Messages::parse(
46        diagnostic,
47        span,
48        &attributes.parameters,
49        &languages,
50        &contents,
51    );
52
53    let current_dir = env::current_dir().expect("Unable to get current directory");
54
55    let relative_path = current_dir
56        .join(file_path)
57        .to_str()
58        .expect("path contains invalid unicode")
59        .to_string();
60
61    attributes
62        .generate(
63            diagnostic,
64            &input.vis,
65            &input.ident,
66            &relative_path,
67            &languages,
68            &messages,
69        )
70        .into()
71}