fehler-macros-1.0.0/.cargo_vcs_info.json0000644000000001121364263261600136620ustar00{ "git": { "sha1": "67a237309a9e943c9de506ba2748501811f4117a" } } fehler-macros-1.0.0/Cargo.toml0000644000000016761364263261600117000ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "fehler-macros" version = "1.0.0" authors = ["Without Boats "] description = "Macros des Fehlers" license = "MIT OR Apache-2.0" repository = "https://github.com/withoutboats/fehler" [lib] proc-macro = true [dependencies.proc-macro2] version = "1.0.0" [dependencies.quote] version = "1.0.0" [dependencies.syn] version = "1.0.0" features = ["default", "fold", "full", "parsing"] fehler-macros-1.0.0/Cargo.toml.orig010064400017500001750000000006231364263261000153530ustar0000000000000000[package] name = "fehler-macros" version = "1.0.0" edition = "2018" authors = ["Without Boats "] license = "MIT OR Apache-2.0" description = "Macros des Fehlers" repository = "https://github.com/withoutboats/fehler" [lib] proc-macro = true [dependencies] quote = "1.0.0" proc-macro2 = "1.0.0" [dependencies.syn] features = ["default", "fold", "full", "parsing"] version = "1.0.0" fehler-macros-1.0.0/src/args.rs010064400017500001750000000071431364263224300145630ustar0000000000000000// The Args type parses the arguments to the `#[throws]` macro. // // It is also responsible for transforming the return type by injecting // the return type and the error type into the wrapper type. use proc_macro2::Span; use syn::{GenericArgument, Path, PathArguments, ReturnType, Token, Type}; use syn::parse::*; const WRAPPER_MUST_BE_PATH: &str = "Wrapper type must be a normal path type"; pub struct Args { error: Option, wrapper: Option, } impl Args { pub fn ret(&mut self, ret: ReturnType) -> ReturnType { let (arrow, ret) = match ret { ReturnType::Default => (arrow(), unit()), ReturnType::Type(arrow, ty) => (arrow, *ty), }; ReturnType::Type(arrow, Box::new(self.inject_to_wrapper(ret))) } fn inject_to_wrapper(&mut self, ret: Type) -> Type { if let Some(Type::Path(mut wrapper)) = self.wrapper.take() { let types = if let Some(error) = self.error.take() { vec![ret, error].into_iter().map(GenericArgument::Type) } else { vec![ret].into_iter().map(GenericArgument::Type) }; match innermost_path_arguments(&mut wrapper.path) { args @ &mut PathArguments::None => { *args = PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { colon2_token: None, lt_token: Token![<](Span::call_site()), args: types.collect(), gt_token: Token![>](Span::call_site()), }); } PathArguments::AngleBracketed(args) => args.args.extend(types), _ => panic!(WRAPPER_MUST_BE_PATH) } Type::Path(wrapper) } else { panic!(WRAPPER_MUST_BE_PATH) } } } impl Parse for Args { fn parse(input: ParseStream) -> Result { if input.is_empty() { return Ok(Args { error: Some(default_error()), wrapper: Some(result()), }) } let error = match input.peek(Token![as]) { true => None, false => { let error = input.parse()?; Some(match error { Type::Infer(_) => default_error(), _ => error, }) } }; let wrapper = Some(match input.parse::().is_ok() { true => input.parse()?, false => result(), }); Ok(Args { error, wrapper }) } } fn innermost_path_arguments(path: &mut Path) -> &mut PathArguments { let arguments = &mut path.segments.last_mut().expect(WRAPPER_MUST_BE_PATH).arguments; match arguments { PathArguments::None => arguments, PathArguments::AngleBracketed(args) => { match args.args.last_mut() { Some(GenericArgument::Type(Type::Path(inner))) => { innermost_path_arguments(&mut inner.path) } // Bizarre cases like `#[throw(_ as MyTryType<'a>)]` just not supported currently _ => panic!("Certain strange wrapper types not supported"), } } _ => panic!(WRAPPER_MUST_BE_PATH) } } fn arrow() -> syn::token::RArrow { Token![->](Span::call_site()) } fn unit() -> Type { syn::parse_str("()").unwrap() } fn result() -> Type { syn::parse_str("::core::result::Result").unwrap() } fn default_error() -> Type { syn::parse_str("Error").unwrap() } fehler-macros-1.0.0/src/lib.rs010064400017500001750000000004421364263224300143700ustar0000000000000000extern crate proc_macro; mod args; mod throws; use proc_macro::*; use args::Args; use throws::Throws; #[proc_macro_attribute] pub fn throws(args: TokenStream, input: TokenStream) -> TokenStream { let args = syn::parse_macro_input!(args as Args); Throws::new(args).fold(input) } fehler-macros-1.0.0/src/throws.rs010064400017500001750000000105351364263224300151540ustar0000000000000000// This module implements the Throws folder. // // The Throws folder actually visits the item being processed and performs two // processes: // - It ok wraps return expressions and inserts terminal Ok(())s. // - It delegates return type rewriting to the Args type. use proc_macro::*; use syn::fold::Fold; use crate::Args; pub struct Throws { args: Args, outer_fn: bool, } impl Throws { pub fn new(args: Args) -> Throws { Throws { args, outer_fn: true } } pub fn fold(&mut self, input: TokenStream) -> TokenStream { if let Ok(item_fn) = syn::parse(input.clone()) { let item_fn = self.fold_item_fn(item_fn); quote::quote!(#item_fn).into() } else if let Ok(method) = syn::parse(input.clone()) { let method = self.fold_impl_item_method(method); quote::quote!(#method).into() } else if let Ok(method) = syn::parse(input.clone()) { let method = self.fold_trait_item_method(method); quote::quote!(#method).into() } else { panic!("#[throws] attribute can only be applied to functions and methods") } } } impl Fold for Throws { fn fold_item_fn(&mut self, mut i: syn::ItemFn) -> syn::ItemFn { if !self.outer_fn { return i; } self.outer_fn = false; modify_tail(is_unit_fn(&i.sig.output), &mut i.block.stmts); let sig = syn::Signature { output: self.fold_return_type(i.sig.output), ..i.sig }; let block = Box::new(self.fold_block(*i.block)); syn::ItemFn { sig, block, ..i } } fn fold_impl_item_method(&mut self, mut i: syn::ImplItemMethod) -> syn::ImplItemMethod { if !self.outer_fn { return i; } self.outer_fn = false; modify_tail(is_unit_fn(&i.sig.output), &mut i.block.stmts); let sig = syn::Signature { output: self.fold_return_type(i.sig.output), ..i.sig }; let block = self.fold_block(i.block); syn::ImplItemMethod { sig, block, ..i } } fn fold_trait_item_method(&mut self, mut i: syn::TraitItemMethod) -> syn::TraitItemMethod { if !self.outer_fn { return i; } self.outer_fn = false; let default = i.default.take().map(|mut block| { modify_tail(is_unit_fn(&i.sig.output), &mut block.stmts); self.fold_block(block) }); let sig = syn::Signature { output: self.fold_return_type(i.sig.output), ..i.sig }; syn::TraitItemMethod { sig, default, ..i } } fn fold_expr_closure(&mut self, i: syn::ExprClosure) -> syn::ExprClosure { i // TODO } fn fold_expr_async(&mut self, i: syn::ExprAsync) -> syn::ExprAsync { i // TODO } fn fold_return_type(&mut self, i: syn::ReturnType) -> syn::ReturnType { self.args.ret(i) } fn fold_expr_return(&mut self, i: syn::ExprReturn) -> syn::ExprReturn { let ok = match &i.expr { Some(expr) => ok(expr), None => ok_unit(), }; syn::ExprReturn { expr: Some(Box::new(ok)), ..i } } } fn modify_tail(is_unit_fn: bool, stmts: &mut Vec) { let last_non_item_stmt = stmts.iter_mut().rev().filter(|s| { if let syn::Stmt::Item(_) = s { false } else { true } }).next(); match last_non_item_stmt { Some(syn::Stmt::Expr(e)) if is_unit_fn => { let new = syn::parse2(quote::quote!(#e;)).unwrap(); stmts.pop(); stmts.push(new); stmts.push(syn::Stmt::Expr(ok_unit())); } Some(syn::Stmt::Expr(e)) => { *e = ok(e); } _ if is_unit_fn => { stmts.push(syn::Stmt::Expr(ok_unit())); } _ => { } } } fn is_unit_fn(i: &syn::ReturnType) -> bool { match i { syn::ReturnType::Default => true, syn::ReturnType::Type(_, ty) => { if let syn::Type::Tuple(syn::TypeTuple { elems, .. }) = &**ty { elems.is_empty() } else { false } } } } fn ok(expr: &syn::Expr) -> syn::Expr { syn::parse2(quote::quote!(<_ as ::fehler::__internal::_Succeed>::from_ok(#expr))).unwrap() } fn ok_unit() -> syn::Expr { syn::parse2(quote::quote!(<_ as ::fehler::__internal::_Succeed>::from_ok(()))).unwrap() }