refactor generated code

This commit is contained in:
Maybe Waffle 2022-08-25 17:01:03 +04:00
parent f03264e694
commit f13e3b6e4a
2 changed files with 73 additions and 53 deletions

View file

@ -41,11 +41,8 @@ pub(crate) fn impl_parse_args_unnamed(
variant: proc_macro2::TokenStream, variant: proc_macro2::TokenStream,
parser_type: &ParserType, parser_type: &ParserType,
) -> proc_macro2::TokenStream { ) -> proc_macro2::TokenStream {
let get_arguments = create_parser( let get_arguments =
parser_type, create_parser(parser_type, data.unnamed.iter().map(|f| &f.ty));
data.unnamed.iter().map(|f| &f.ty),
data.unnamed.len(),
);
let iter = (0..data.unnamed.len()).map(syn::Index::from); let iter = (0..data.unnamed.len()).map(syn::Index::from);
let mut initialization = quote! {}; let mut initialization = quote! {};
for i in iter { for i in iter {
@ -65,12 +62,9 @@ pub(crate) fn impl_parse_args_named(
variant: proc_macro2::TokenStream, variant: proc_macro2::TokenStream,
parser_type: &ParserType, parser_type: &ParserType,
) -> proc_macro2::TokenStream { ) -> proc_macro2::TokenStream {
let get_arguments = create_parser( let get_arguments =
parser_type, create_parser(parser_type, data.named.iter().map(|f| &f.ty));
data.named.iter().map(|f| &f.ty), let i = (0..).map(syn::Index::from);
data.named.len(),
);
let i = (0..data.named.len()).map(syn::Index::from);
let name = data.named.iter().map(|f| f.ident.as_ref().unwrap()); let name = data.named.iter().map(|f| f.ident.as_ref().unwrap());
let res = quote! { let res = quote! {
{ {
@ -83,26 +77,30 @@ pub(crate) fn impl_parse_args_named(
fn create_parser<'a>( fn create_parser<'a>(
parser_type: &ParserType, parser_type: &ParserType,
mut types: impl Iterator<Item = &'a Type>, mut types: impl ExactSizeIterator<Item = &'a Type>,
count_args: usize,
) -> proc_macro2::TokenStream { ) -> proc_macro2::TokenStream {
let function_to_parse = match parser_type { let function_to_parse = match parser_type {
ParserType::Default => match count_args { ParserType::Default => match types.len() {
1 => { 1 => {
let ty = types.next().expect("count_args != types.len()"); let ty = types.next().unwrap();
quote! { (|s: String| { quote! {
let res = <#ty>::from_str(&s) (
.map_err(|e|ParseError::IncorrectFormat({ let e: Box<dyn std::error::Error + Send + Sync + 'static> = e.into(); e }))?; |s: String| {
Ok((res, )) let res = <#ty>::from_str(&s)
}) .map_err(|e| ParseError::IncorrectFormat(e.into()))?;
Ok((res,))
}
)
} }
} }
_ => quote! { compile_error!("Expected exactly 1 argument") }, _ => {
quote! { compile_error!("Default parser works only with exactly 1 field") }
}
}, },
ParserType::Split { separator } => parser_with_separator( ParserType::Split { separator } => parser_with_separator(
&separator.clone().unwrap_or_else(|| " ".to_owned()), &separator.clone().unwrap_or_else(|| " ".to_owned()),
types, types,
count_args,
), ),
ParserType::Custom(s) => { ParserType::Custom(s) => {
let path = syn::parse_str::<syn::Path>(s).unwrap_or_else(|_| { let path = syn::parse_str::<syn::Path>(s).unwrap_or_else(|_| {
@ -111,6 +109,7 @@ fn create_parser<'a>(
quote! { #path } quote! { #path }
} }
}; };
quote! { quote! {
let arguments = #function_to_parse(args)?; let arguments = #function_to_parse(args)?;
} }
@ -118,31 +117,46 @@ fn create_parser<'a>(
fn parser_with_separator<'a>( fn parser_with_separator<'a>(
separator: &str, separator: &str,
types: impl Iterator<Item = &'a Type>, types: impl ExactSizeIterator<Item = &'a Type>,
count_args: usize,
) -> proc_macro2::TokenStream { ) -> proc_macro2::TokenStream {
let inner = quote! { let mut splited = s.split(#separator); }; let expected = types.len();
let i = 0..count_args; let res = {
let inner2 = quote! { let found = 0usize..;
#(<#types>::from_str(splited.next().ok_or(ParseError::TooFewArguments { quote! {
expected: #count_args, (
found: #i, #(
message: format!("Expected but not found arg number {}", #i + 1), {
})?).map_err(|e|ParseError::IncorrectFormat({ let e: Box<dyn std::error::Error + Send + Sync + 'static> = e.into(); e }))?,)* let s = splitted.next().ok_or(ParseError::TooFewArguments {
expected: #expected,
found: #found,
message: format!("Expected but not found arg number {}", #found + 1),
})?;
<#types>::from_str(s).map_err(|e| ParseError::IncorrectFormat(e.into()))?
}
),*
)
}
}; };
let res = quote! { let res = quote! {
(|s: String| { (
#inner |s: String| {
let res = (#inner2); let mut splitted = s.split(#separator);
match splited.next() {
Some(d) => Err(ParseError::TooManyArguments { let res = #res;
expected: #count_args,
found: #count_args + 1, match splitted.next() {
message: format!("Excess argument: {}", d), Some(d) => Err(ParseError::TooManyArguments {
}), expected: #expected,
None => Ok(res) found: #expected + 1,
message: format!("Excess argument: {}", d),
}),
None => Ok(res)
}
} }
}) )
}; };
res res
} }

View file

@ -131,25 +131,31 @@ fn impl_parse(
where where
N: Into<String> N: Into<String>
{ {
// FIXME: we should probably just call a helper function from `teloxide`, instead of parsing command syntax ourselves
use std::str::FromStr; use std::str::FromStr;
use teloxide::utils::command::ParseError; use teloxide::utils::command::ParseError;
// 2 is used to only split once (=> in two parts),
// we only need to split the command and the rest of arguments.
let mut words = s.splitn(2, ' '); let mut words = s.splitn(2, ' ');
let mut splitted = words.next().expect("First item will be always.").split('@');
let command_raw = splitted.next().expect("First item will be always."); // Unwrap: split iterators always have at least one item
let bot = splitted.next(); let mut full_command = words.next().unwrap().split('@');
let bot_name = bot_name.into(); let command = full_command.next().unwrap();
match bot {
Some(name) if name.eq_ignore_ascii_case(&bot_name) => {} let bot_username = full_command.next();
match bot_username {
None => {} None => {}
Some(n) => return Err(ParseError::WrongBotName(n.to_string())), Some(username) if username.eq_ignore_ascii_case(&bot_name.into()) => {}
Some(n) => return Err(ParseError::WrongBotName(n.to_owned())),
} }
let mut args = words.next().unwrap_or("").to_string();
match command_raw { let args = words.next().unwrap_or("").to_owned();
match command {
#( #(
#matching_values => Ok(#variants_initialization), #matching_values => Ok(#variants_initialization),
)* )*
_ => Err(ParseError::UnknownCommand(command_raw.to_string())), _ => Err(ParseError::UnknownCommand(command.to_owned())),
} }
} }
} }