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 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<TokenStream, Er
let definition = match definition {
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)?;
@ -34,7 +34,7 @@ pub fn api_macro(attr: TokenStream, item: TokenStream) -> Result<TokenStream, Er
Ok(output)
}
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,
) -> Result<TokenStream, Error> {
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!
}

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;
#[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::<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
/// 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),
}
}