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

View file

@ -131,25 +131,31 @@ fn impl_parse(
where
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 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 splitted = words.next().expect("First item will be always.").split('@');
let command_raw = splitted.next().expect("First item will be always.");
let bot = splitted.next();
let bot_name = bot_name.into();
match bot {
Some(name) if name.eq_ignore_ascii_case(&bot_name) => {}
// Unwrap: split iterators always have at least one item
let mut full_command = words.next().unwrap().split('@');
let command = full_command.next().unwrap();
let bot_username = full_command.next();
match bot_username {
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),
)*
_ => Err(ParseError::UnknownCommand(command_raw.to_string())),
_ => Err(ParseError::UnknownCommand(command.to_owned())),
}
}
}