local_fmt_macros_internal/def_local_fmt/
arg.rs

1use 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
75#[derive(Debug)]
76pub enum ArgPath {
77    File(PathBuf),
78    Folder(PathBuf),
79}
80
81impl syn::parse::Parse for Args {
82    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
83        mod kw {
84            syn::custom_keyword!(name);
85            syn::custom_keyword!(lang);
86            syn::custom_keyword!(message);
87            syn::custom_keyword!(supplier);
88            syn::custom_keyword!(file_type);
89            syn::custom_keyword!(lang_file);
90            syn::custom_keyword!(lang_folder);
91        }
92
93        macro_rules! parse {
94            ($ident:ident) => {
95                parse!($ident, Ident);
96            };
97            ($ident:ident, $ty:ty) => {
98                parse!($ident, $ty, without_comma);
99                let _: syn::Token![,] = input.parse()?;
100            };
101            ($ident:ident, $ty:ty, without_comma) => {
102                let _: kw::$ident = input.parse()?;
103                let _: syn::Token![=] = input.parse()?;
104                let $ident: $ty = input.parse()?;
105            };
106        }
107
108        parse!(name);
109        parse!(lang);
110        parse!(message, MessageField, without_comma);
111
112        if message.fields.is_some() {
113            let _: syn::Token![,] = input.parse()?;
114        }
115
116        parse!(supplier, syn::Expr);
117
118        parse!(file_type, ArgFileType);
119
120        let crate_root = {
121            #[allow(clippy::panic)]
122            let crate_root = std::env::var("CARGO_MANIFEST_DIR")
123                .unwrap_or_else(|_| panic!("failed to get CARGO_MANIFEST_DIR"));
124            PathBuf::from(crate_root)
125        };
126
127        let path = if input.peek(kw::lang_file) {
128            parse!(lang_file, syn::LitStr, without_comma);
129
130            ArgPath::File(crate_root.join(lang_file.value()))
131        } else if input.peek(kw::lang_folder) {
132            parse!(lang_folder, syn::LitStr, without_comma);
133
134            ArgPath::Folder(crate_root.join(lang_folder.value()))
135        } else {
136            return Err(input.error("expected lang_file or lang_folder"));
137        };
138
139        if input.peek(syn::Token![,]) {
140            let _ = input.parse::<syn::Token![,]>()?;
141        }
142
143        Ok(Self {
144            name,
145            lang,
146            message,
147            supplier,
148            file_type,
149            path,
150        })
151    }
152}