local_fmt_macros_internal/def_local_fmt/
arg.rs1use std::path::PathBuf;
2
3use syn::parse::ParseStream;
4use syn::{Ident, LitStr};
5
6#[derive(Debug)]
7pub struct MessageField {
8 pub ty: Ident,
9 pub fields: Option<Vec<(Ident, MessageField)>>,
10}
11
12impl syn::parse::Parse for MessageField {
13 fn parse(input: ParseStream) -> syn::Result<Self> {
14 let ty: Ident = input.parse()?;
15 if !input.peek(syn::token::Brace) {
16 if !input.is_empty() {
17 let _: syn::Token![,] = input.parse()?;
18 }
19 return Ok(Self { ty, fields: None });
20 };
21 let content;
22 syn::braced!(content in input);
23
24 let mut fields = Vec::new();
25 while !content.is_empty() {
26 let ty: Ident = content.parse()?;
27
28 let _: syn::Token![:] = content.parse()?;
29
30 let field: MessageField = content.parse()?;
31
32 fields.push((ty, field));
33
34 if content.is_empty() {
35 break;
36 }
37
38 let _: syn::Token![,] = content.parse()?;
39 }
40
41 Ok(Self {
42 ty,
43 fields: Some(fields),
44 })
45 }
46}
47
48pub struct Args {
49 pub name: Ident,
50 pub lang: Ident,
51 pub message: MessageField,
52 pub supplier: syn::Expr,
53 pub file_type: ArgFileType,
54 pub path: ArgPath,
55}
56
57pub enum ArgFileType {
58 Toml,
59 Json,
60 Yaml,
61}
62
63impl syn::parse::Parse for ArgFileType {
64 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
65 let lit: LitStr = input.parse()?;
66 match lit.value().as_str() {
67 "toml" => Ok(Self::Toml),
68 "json" => Ok(Self::Json),
69 "yaml" => Ok(Self::Yaml),
70 _ => Err(syn::Error::new(lit.span(), "expected toml or json")),
71 }
72 }
73}
74
75pub enum ArgPath {
76 File(PathBuf),
77 Folder(PathBuf),
78}
79
80impl syn::parse::Parse for Args {
81 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
82 mod kw {
83 syn::custom_keyword!(name);
84 syn::custom_keyword!(lang);
85 syn::custom_keyword!(message);
86 syn::custom_keyword!(supplier);
87 syn::custom_keyword!(file_type);
88 syn::custom_keyword!(lang_file);
89 syn::custom_keyword!(lang_folder);
90 }
91
92 macro_rules! parse {
93 ($ident:ident) => {
94 parse!($ident, Ident);
95 };
96 ($ident:ident, $ty:ty) => {
97 parse!($ident, $ty, without_comma);
98 let _: syn::Token![,] = input.parse()?;
99 };
100 ($ident:ident, $ty:ty, without_comma) => {
101 let _: kw::$ident = input.parse()?;
102 let _: syn::Token![=] = input.parse()?;
103 let $ident: $ty = input.parse()?;
104 };
105 }
106
107 parse!(name);
108 parse!(lang);
109 parse!(message, MessageField, without_comma);
110
111 if message.fields.is_some() {
112 let _: syn::Token![,] = input.parse()?;
113 }
114
115 parse!(supplier, syn::Expr);
116
117 parse!(file_type, ArgFileType);
118
119 let crate_root = {
120 #[allow(clippy::panic)]
121 let crate_root = std::env::var("CARGO_MANIFEST_DIR")
122 .unwrap_or_else(|_| panic!("failed to get CARGO_MANIFEST_DIR"));
123 PathBuf::from(crate_root)
124 };
125
126 let path = if input.peek(kw::lang_file) {
127 parse!(lang_file, syn::LitStr, without_comma);
128
129 ArgPath::File(crate_root.join(lang_file.value()))
130 } else if input.peek(kw::lang_folder) {
131 parse!(lang_folder, syn::LitStr, without_comma);
132
133 ArgPath::Folder(crate_root.join(lang_folder.value()))
134 } else {
135 return Err(input.error("expected lang_file or lang_folder"));
136 };
137
138 if input.peek(syn::Token![,]) {
139 let _ = input.parse::<syn::Token![,]>()?;
140 }
141
142 Ok(Self {
143 name,
144 lang,
145 message,
146 supplier,
147 file_type,
148 path,
149 })
150 }
151}