Improve error handling by adding custom Error type

This commit is contained in:
Maybe Waffle 2022-08-22 15:55:25 +04:00
parent 3580fd35c5
commit b16256b8ea
4 changed files with 48 additions and 28 deletions

View file

@ -3,9 +3,10 @@ use crate::{
command_enum::CommandEnum, command_enum::CommandEnum,
fields_parse::ParserType, fields_parse::ParserType,
rename_rules::rename_by_rule, rename_rules::rename_by_rule,
Result,
}; };
pub struct Command { pub(crate) struct Command {
pub prefix: Option<String>, pub prefix: Option<String>,
pub description: Option<String>, pub description: Option<String>,
pub parser: Option<ParserType>, pub parser: Option<ParserType>,
@ -14,7 +15,7 @@ pub struct Command {
} }
impl Command { impl Command {
pub fn try_from(attrs: &[Attr], name: &str) -> Result<Self, String> { pub fn try_from(attrs: &[Attr], name: &str) -> Result<Self> {
let attrs = parse_attrs(attrs)?; let attrs = parse_attrs(attrs)?;
let mut new_name = name.to_string(); let mut new_name = name.to_string();
let mut renamed = false; let mut renamed = false;
@ -64,7 +65,7 @@ impl Command {
} }
} }
pub struct CommandAttrs { pub(crate) struct CommandAttrs {
pub(crate) prefix: Option<String>, pub(crate) prefix: Option<String>,
pub(crate) description: Option<String>, pub(crate) description: Option<String>,
pub(crate) rename: Option<String>, pub(crate) rename: Option<String>,
@ -72,7 +73,7 @@ pub struct CommandAttrs {
pub(crate) separator: Option<String>, pub(crate) separator: Option<String>,
} }
pub fn parse_attrs(attrs: &[Attr]) -> Result<CommandAttrs, String> { pub(crate) fn parse_attrs(attrs: &[Attr]) -> Result<CommandAttrs> {
let mut prefix = None; let mut prefix = None;
let mut description = None; let mut description = None;
let mut rename_rule = None; let mut rename_rule = None;

View file

@ -1,7 +1,10 @@
use crate::{attr::Attr, command::parse_attrs, fields_parse::ParserType}; use crate::{
attr::Attr, command::parse_attrs, error::compile_error,
fields_parse::ParserType, Result,
};
#[derive(Debug)] #[derive(Debug)]
pub struct CommandEnum { pub(crate) struct CommandEnum {
pub prefix: Option<String>, pub prefix: Option<String>,
pub description: Option<String>, pub description: Option<String>,
pub rename_rule: Option<String>, pub rename_rule: Option<String>,
@ -9,7 +12,7 @@ pub struct CommandEnum {
} }
impl CommandEnum { impl CommandEnum {
pub fn try_from(attrs: &[Attr]) -> Result<Self, String> { pub fn try_from(attrs: &[Attr]) -> Result<Self> {
let attrs = parse_attrs(attrs)?; let attrs = parse_attrs(attrs)?;
let prefix = attrs.prefix; let prefix = attrs.prefix;
@ -32,7 +35,7 @@ impl CommandEnum {
| "SCREAMING_SNAKE_CASE" | "SCREAMING_SNAKE_CASE"
| "kebab-case" | "kebab-case"
| "SCREAMING-KEBAB-CASE" => {} | "SCREAMING-KEBAB-CASE" => {}
_ => return Err("disallowed value".to_owned()), _ => return Err(compile_error("disallowed value")),
} }
} }
Ok(Self { Ok(Self {

25
src/error.rs Normal file
View file

@ -0,0 +1,25 @@
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
pub(crate) type Result<T, E = Error> = std::result::Result<T, E>;
pub(crate) struct Error(TokenStream);
pub(crate) fn compile_error<T>(data: T) -> Error
where
T: ToTokens,
{
Error(TokenStream::from(quote! { compile_error!(#data) }))
}
impl From<Error> for proc_macro::TokenStream {
fn from(Error(e): Error) -> Self {
e.into()
}
}
impl From<syn::Error> for Error {
fn from(e: syn::Error) -> Self {
Self(e.to_compile_error())
}
}

View file

@ -3,6 +3,7 @@
mod attr; mod attr;
mod command; mod command;
mod command_enum; mod command_enum;
mod error;
mod fields_parse; mod fields_parse;
mod rename_rules; mod rename_rules;
@ -16,22 +17,22 @@ use crate::{
fields_parse::{impl_parse_args_named, impl_parse_args_unnamed}, fields_parse::{impl_parse_args_named, impl_parse_args_unnamed},
}; };
use proc_macro::TokenStream; use proc_macro::TokenStream;
use quote::{quote, ToTokens}; use quote::quote;
use syn::{DeriveInput, Fields}; use syn::{DeriveInput, Fields};
pub(crate) use error::{compile_error, Error, Result};
#[proc_macro_derive(BotCommands, attributes(command))] #[proc_macro_derive(BotCommands, attributes(command))]
pub fn bot_commands_derive(tokens: TokenStream) -> TokenStream { pub fn bot_commands_derive(tokens: TokenStream) -> TokenStream {
bot_commands_impl(tokens).unwrap_or_else(|err| err) bot_commands_impl(tokens).unwrap_or_else(Error::into)
} }
fn bot_commands_impl(tokens: TokenStream) -> Result<TokenStream, TokenStream> { fn bot_commands_impl(tokens: TokenStream) -> Result<TokenStream, Error> {
let input = syn::parse_macro_input::parse::<DeriveInput>(tokens) let input = syn::parse_macro_input::parse::<DeriveInput>(tokens)?;
.map_err(|e| compile_error(e.to_compile_error()))?;
let data_enum: &syn::DataEnum = get_enum_data(&input)?; let data_enum: &syn::DataEnum = get_enum_data(&input)?;
let enum_attrs: Vec<Attr> = parse_attributes(&input.attrs)?; let enum_attrs: Vec<Attr> = parse_attributes(&input.attrs)?;
let command_enum = let command_enum = CommandEnum::try_from(enum_attrs.as_slice())?;
CommandEnum::try_from(enum_attrs.as_slice()).map_err(compile_error)?;
let variants: Vec<&syn::Variant> = data_enum.variants.iter().collect(); let variants: Vec<&syn::Variant> = data_enum.variants.iter().collect();
@ -45,8 +46,7 @@ fn bot_commands_impl(tokens: TokenStream) -> Result<TokenStream, TokenStream> {
attrs.append(attrs_.data.as_mut()); attrs.append(attrs_.data.as_mut());
} }
let command = let command =
Command::try_from(attrs.as_slice(), &variant.ident.to_string()) Command::try_from(attrs.as_slice(), &variant.ident.to_string())?;
.map_err(compile_error)?;
variant_infos.push(command); variant_infos.push(command);
} }
@ -176,16 +176,14 @@ fn impl_parse(
} }
} }
fn get_enum_data(input: &DeriveInput) -> Result<&syn::DataEnum, TokenStream> { fn get_enum_data(input: &DeriveInput) -> Result<&syn::DataEnum> {
match &input.data { match &input.data {
syn::Data::Enum(data) => Ok(data), syn::Data::Enum(data) => Ok(data),
_ => Err(compile_error("TelegramBotCommand allowed only for enums")), _ => Err(compile_error("TelegramBotCommand allowed only for enums")),
} }
} }
fn parse_attributes( fn parse_attributes(input: &[syn::Attribute]) -> Result<Vec<Attr>> {
input: &[syn::Attribute],
) -> Result<Vec<Attr>, TokenStream> {
let mut enum_attrs = Vec::new(); let mut enum_attrs = Vec::new();
for attr in input.iter() { for attr in input.iter() {
match attr.parse_args::<VecAttrs>() { match attr.parse_args::<VecAttrs>() {
@ -199,10 +197,3 @@ fn parse_attributes(
} }
Ok(enum_attrs) Ok(enum_attrs)
} }
fn compile_error<T>(data: T) -> TokenStream
where
T: ToTokens,
{
TokenStream::from(quote! { compile_error!(#data) })
}