macro: helpers for error handling

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2019-07-18 10:28:24 +02:00
parent a1c8b00f17
commit 145abf62a5
3 changed files with 44 additions and 9 deletions

View File

@ -3,8 +3,8 @@ use std::collections::HashMap;
use proc_macro2::{Delimiter, Ident, Span, TokenStream, TokenTree}; use proc_macro2::{Delimiter, Ident, Span, TokenStream, TokenTree};
use failure::{bail, format_err, Error}; use failure::{bail, format_err, Error};
use quote::{quote, quote_spanned, ToTokens}; use quote::{quote, ToTokens};
use syn::{Expr, Token}; use syn::{spanned::Spanned, Expr, Token};
use super::api_def::{CommonTypeDefinition, ParameterDefinition}; use super::api_def::{CommonTypeDefinition, ParameterDefinition};
use super::parsing::*; use super::parsing::*;
@ -17,7 +17,7 @@ pub fn api_macro(attr: TokenStream, item: TokenStream) -> Result<TokenStream, Er
let definition = match definition { let definition = match definition {
TokenTree::Group(ref group) if group.delimiter() == Delimiter::Brace => group.stream(), TokenTree::Group(ref group) if group.delimiter() == Delimiter::Brace => group.stream(),
_ => bail!("expected api definition in braces"), _ => cbail!(definition.span() => "expected api definition in braces"),
}; };
let definition = parse_object(definition)?; let definition = parse_object(definition)?;
@ -34,7 +34,7 @@ pub fn api_macro(attr: TokenStream, item: TokenStream) -> Result<TokenStream, Er
Ok(output) Ok(output)
} }
syn::Item::Fn(func) => handle_function(definition, func), syn::Item::Fn(func) => 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, mut item: syn::ItemFn,
) -> Result<TokenStream, Error> { ) -> Result<TokenStream, Error> {
if item.decl.generics.lt_token.is_some() { if item.decl.generics.lt_token.is_some() {
return Ok(quote_spanned! { item.decl.generics.lt_token.unwrap().span => cbail!(
compile_error!("cannot use generic functions for api macros currently"); item.decl.generics.span(),
}.into()); "cannot use generic functions for api macros currently",
);
// Not until we stabilize our generated representation! // Not until we stabilize our generated representation!
} }

View File

@ -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)*) }
}

View File

@ -5,6 +5,9 @@ extern crate proc_macro2;
use proc_macro::TokenStream; use proc_macro::TokenStream;
#[macro_use]
mod error;
mod api_def; mod api_def;
mod parsing; mod parsing;
mod util; mod util;
@ -12,6 +15,13 @@ mod util;
mod api_macro; mod api_macro;
mod router_macro; mod router_macro;
fn handle_error(kind: &'static str, err: failure::Error) -> TokenStream {
match err.downcast::<error::CompileError>() {
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 /// 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 /// 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 /// 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 { pub fn api(attr: TokenStream, item: TokenStream) -> TokenStream {
match api_macro::api_macro(attr.into(), item.into()) { match api_macro::api_macro(attr.into(), item.into()) {
Ok(output) => output.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... // TODO...
match router_macro::router_macro(input.into()) { match router_macro::router_macro(input.into()) {
Ok(output) => output.into(), Ok(output) => output.into(),
Err(err) => panic!("error in router macro: {}", err), Err(err) => handle_error("router", err),
} }
} }