1use proc_macro::{Span, TokenStream};
2use quote::quote;
3use syn::{parse_macro_input, Ident};
4
5fn capitalize(input: &str) -> String {
6 let mut chars = input.chars();
7 match chars.next() {
8 None => String::new(),
9 Some(f) => f.to_uppercase().chain(chars).collect(),
10 }
11}
12
13#[proc_macro]
14pub fn time_parser(token_stream: TokenStream) -> TokenStream {
15 let lang = parse_macro_input!(token_stream as syn::Path);
16 let lang = lang.get_ident().unwrap();
17
18 let span = Span::call_site().into();
19 let fn_name = Ident::new(&format!("parse_time_{}", lang), span);
20 let lang_capitalized = capitalize(&lang.to_string());
21
22 let error_name = Ident::new(&format!("{}TimeParseError", lang_capitalized), span);
23 let time_parser_name = Ident::new(&format!("{}TimeParser", lang_capitalized), span);
24
25 let expanded = quote! {
26 use pest::iterators::Pair;
27 use pest::Parser;
28
29
30
31 pub fn #fn_name(
32 input: &str,
33 ) -> Result<crate::#lang::Time, crate::#lang::#error_name> {
34 let pairs =
35 crate::#lang::#time_parser_name::parse(crate::#lang::Rule::time, input)?;
36 let pairs = pairs.flatten().collect::<Vec<Pair<crate::#lang::Rule>>>();
37
38 let rules_and_str = pairs
39 .iter()
40 .map(|pair| (pair.as_rule(), pair.as_str()))
41 .collect::<Vec<(crate::#lang::Rule, &str)>>();
42
43 match rules_and_str.as_slice() {
44 [(crate::#lang::Rule::now, _), (crate::#lang::Rule::EOI, _)] => {
45 Ok(crate::#lang::Time::Now)
46 }
47 _ => Err(crate::#lang::#error_name::UnexpectedPattern),
48 }
49 }
50 };
51
52 TokenStream::from(expanded)
53}