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,
fields_parse::ParserType,
rename_rules::rename_by_rule,
Result,
};
pub struct Command {
pub(crate) struct Command {
pub prefix: Option<String>,
pub description: Option<String>,
pub parser: Option<ParserType>,
@ -14,7 +15,7 @@ pub struct 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 mut new_name = name.to_string();
let mut renamed = false;
@ -64,7 +65,7 @@ impl Command {
}
}
pub struct CommandAttrs {
pub(crate) struct CommandAttrs {
pub(crate) prefix: Option<String>,
pub(crate) description: Option<String>,
pub(crate) rename: Option<String>,
@ -72,7 +73,7 @@ pub struct CommandAttrs {
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 description = 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)]
pub struct CommandEnum {
pub(crate) struct CommandEnum {
pub prefix: Option<String>,
pub description: Option<String>,
pub rename_rule: Option<String>,
@ -9,7 +12,7 @@ pub struct 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 prefix = attrs.prefix;
@ -32,7 +35,7 @@ impl CommandEnum {
| "SCREAMING_SNAKE_CASE"
| "kebab-case"
| "SCREAMING-KEBAB-CASE" => {}
_ => return Err("disallowed value".to_owned()),
_ => return Err(compile_error("disallowed value")),
}
}
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 command;
mod command_enum;
mod error;
mod fields_parse;
mod rename_rules;
@ -16,22 +17,22 @@ use crate::{
fields_parse::{impl_parse_args_named, impl_parse_args_unnamed},
};
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use quote::quote;
use syn::{DeriveInput, Fields};
pub(crate) use error::{compile_error, Error, Result};
#[proc_macro_derive(BotCommands, attributes(command))]
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> {
let input = syn::parse_macro_input::parse::<DeriveInput>(tokens)
.map_err(|e| compile_error(e.to_compile_error()))?;
fn bot_commands_impl(tokens: TokenStream) -> Result<TokenStream, Error> {
let input = syn::parse_macro_input::parse::<DeriveInput>(tokens)?;
let data_enum: &syn::DataEnum = get_enum_data(&input)?;
let enum_attrs: Vec<Attr> = parse_attributes(&input.attrs)?;
let command_enum =
CommandEnum::try_from(enum_attrs.as_slice()).map_err(compile_error)?;
let command_enum = CommandEnum::try_from(enum_attrs.as_slice())?;
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());
}
let command =
Command::try_from(attrs.as_slice(), &variant.ident.to_string())
.map_err(compile_error)?;
Command::try_from(attrs.as_slice(), &variant.ident.to_string())?;
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 {
syn::Data::Enum(data) => Ok(data),
_ => Err(compile_error("TelegramBotCommand allowed only for enums")),
}
}
fn parse_attributes(
input: &[syn::Attribute],
) -> Result<Vec<Attr>, TokenStream> {
fn parse_attributes(input: &[syn::Attribute]) -> Result<Vec<Attr>> {
let mut enum_attrs = Vec::new();
for attr in input.iter() {
match attr.parse_args::<VecAttrs>() {
@ -199,10 +197,3 @@ fn parse_attributes(
}
Ok(enum_attrs)
}
fn compile_error<T>(data: T) -> TokenStream
where
T: ToTokens,
{
TokenStream::from(quote! { compile_error!(#data) })
}