1#![forbid(unsafe_code)]
2#![forbid(missing_docs)]
3#![warn(clippy::pedantic)]
4
5use 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#[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}