From 145abf62a51ddd700e3580bd76d22dfa2be344d4 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Thu, 18 Jul 2019 10:28:24 +0200 Subject: [PATCH] macro: helpers for error handling Signed-off-by: Wolfgang Bumiller --- proxmox-api-macro/src/api_macro.rs | 15 ++++++++------- proxmox-api-macro/src/error.rs | 24 ++++++++++++++++++++++++ proxmox-api-macro/src/lib.rs | 14 ++++++++++++-- 3 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 proxmox-api-macro/src/error.rs diff --git a/proxmox-api-macro/src/api_macro.rs b/proxmox-api-macro/src/api_macro.rs index 8db4f51b..25098910 100644 --- a/proxmox-api-macro/src/api_macro.rs +++ b/proxmox-api-macro/src/api_macro.rs @@ -3,8 +3,8 @@ use std::collections::HashMap; use proc_macro2::{Delimiter, Ident, Span, TokenStream, TokenTree}; use failure::{bail, format_err, Error}; -use quote::{quote, quote_spanned, ToTokens}; -use syn::{Expr, Token}; +use quote::{quote, ToTokens}; +use syn::{spanned::Spanned, Expr, Token}; use super::api_def::{CommonTypeDefinition, ParameterDefinition}; use super::parsing::*; @@ -17,7 +17,7 @@ pub fn api_macro(attr: TokenStream, item: TokenStream) -> Result group.stream(), - _ => bail!("expected api definition in braces"), + _ => cbail!(definition.span() => "expected api definition in braces"), }; let definition = parse_object(definition)?; @@ -34,7 +34,7 @@ pub fn api_macro(attr: TokenStream, item: TokenStream) -> Result handle_function(definition, func), - _ => bail!("api macro currently only applies to structs and functions"), + _ => cbail!(item.span() => "api macro currently only applies to structs and functions"), } } @@ -43,9 +43,10 @@ fn handle_function( mut item: syn::ItemFn, ) -> Result { if item.decl.generics.lt_token.is_some() { - return Ok(quote_spanned! { item.decl.generics.lt_token.unwrap().span => - compile_error!("cannot use generic functions for api macros currently"); - }.into()); + cbail!( + item.decl.generics.span(), + "cannot use generic functions for api macros currently", + ); // Not until we stabilize our generated representation! } diff --git a/proxmox-api-macro/src/error.rs b/proxmox-api-macro/src/error.rs new file mode 100644 index 00000000..f239aee0 --- /dev/null +++ b/proxmox-api-macro/src/error.rs @@ -0,0 +1,24 @@ +#[derive(Debug)] +pub struct CompileError { + pub tokens: proc_macro::TokenStream, +} + +unsafe impl Send for CompileError {} +unsafe impl Sync for CompileError {} + +impl std::fmt::Display for CompileError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "generic compile error") + } +} + +impl std::error::Error for CompileError {} + +macro_rules! cbail { + ($span:expr => $($msg:tt)*) => { + return Err(::failure::Error::from(crate::error::CompileError { + tokens: ::quote::quote_spanned! { $span => compile_error!($($msg)*); }.into() + })) + }; + ($span:expr, $($msg:tt)*) => { cbail!($span => $($msg)*) } +} diff --git a/proxmox-api-macro/src/lib.rs b/proxmox-api-macro/src/lib.rs index e820ede2..57b546ba 100644 --- a/proxmox-api-macro/src/lib.rs +++ b/proxmox-api-macro/src/lib.rs @@ -5,6 +5,9 @@ extern crate proc_macro2; use proc_macro::TokenStream; +#[macro_use] +mod error; + mod api_def; mod parsing; mod util; @@ -12,6 +15,13 @@ mod util; mod api_macro; mod router_macro; +fn handle_error(kind: &'static str, err: failure::Error) -> TokenStream { + match err.downcast::() { + Ok(err) => err.tokens, + Err(err) => panic!("error in {}: {}", kind, err), + } +} + /// This is the `#[api(api definition)]` attribute for functions. An Api definition defines the /// parameters and return type of an API call. The function will automatically be wrapped in a /// function taking and returning a json `Value`, while performing validity checks on both input @@ -44,7 +54,7 @@ mod router_macro; pub fn api(attr: TokenStream, item: TokenStream) -> TokenStream { match api_macro::api_macro(attr.into(), item.into()) { Ok(output) => output.into(), - Err(err) => panic!("error in api definition: {}", err), + Err(err) => handle_error("api definition", err), } } @@ -135,6 +145,6 @@ pub fn router(input: TokenStream) -> TokenStream { // TODO... match router_macro::router_macro(input.into()) { Ok(output) => output.into(), - Err(err) => panic!("error in router macro: {}", err), + Err(err) => handle_error("router", err), } }