├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── rustfmt.toml ├── src └── lib.rs └── tests ├── enum_error.rs ├── into_enum.rs └── newtype.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | *.rustfmt 5 | rusty-tags.* 6 | 7 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 8 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 9 | Cargo.lock 10 | 11 | # cargo fmt 12 | *.bk 13 | 14 | # test 15 | test_* 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | rust: 4 | - stable 5 | - beta 6 | - nightly 7 | 8 | matrix: 9 | allow_failures: 10 | - rust: nightly 11 | 12 | script: 13 | - cargo build --verbose 14 | - cargo test --verbose 15 | 16 | env: 17 | - RUST_BACKTRACE=1 18 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "procedurals" 3 | version = "0.3.2-alpha.0" 4 | authors = ["Toshiki Teramura "] 5 | 6 | description = "Collection of proc-macros" 7 | documentation = "https://docs.rs/procedurals/" 8 | repository = "https://github.com/termoshtt/procedurals" 9 | keywords = ["proc_macro"] 10 | license = "MIT" 11 | 12 | [lib] 13 | proc-macro = true 14 | 15 | [dependencies] 16 | syn = "0.14" 17 | quote = "0.6" 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Toshiki Teramura 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Procedurals 2 | =============== 3 | [![Crate](http://meritbadge.herokuapp.com/procedurals)](https://crates.io/crates/procedurals) 4 | [![docs.rs](https://docs.rs/procedurals/badge.svg)](https://docs.rs/procedurals) 5 | [![Build Status](https://travis-ci.org/termoshtt/procedurals.svg?branch=master)](https://travis-ci.org/termoshtt/procedurals) 6 | 7 | Collection of basic proc-macros 8 | 9 | 10 | IntoEnum 11 | --------- 12 | 13 | ```rust 14 | #[macro_use] 15 | extern crate procedurals; 16 | 17 | struct A {} 18 | struct B {} 19 | 20 | #[derive(IntoEnum)] // derives From and From for E 21 | enum E { 22 | A(A), 23 | B(B), 24 | } 25 | ``` 26 | 27 | EnumError 28 | ---------- 29 | 30 | ```rust 31 | #[macro_use] 32 | extern crate procedurals; 33 | use std::{io, fmt}; 34 | 35 | #[derive(Debug, EnumError)] // EnumError derives From<*>, fmt::Display and error::Error 36 | pub enum Error { 37 | IO(io::Error), 38 | Fmt(fmt::Error), 39 | } 40 | ``` 41 | 42 | NewType 43 | --------- 44 | 45 | ```rust 46 | #[macro_use] 47 | extern crate procedurals; 48 | 49 | struct B {} 50 | 51 | #[derive(NewType)] // NewType derives From, Into, Deref, and DerefMut 52 | struct A(B); 53 | ``` 54 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | verbose = false 2 | disable_all_formatting = false 3 | skip_children = false 4 | max_width = 120 5 | error_on_line_overflow = true 6 | tab_spaces = 4 7 | fn_call_width = 60 8 | struct_lit_width = 18 9 | struct_variant_width = 35 10 | force_explicit_abi = true 11 | newline_style = "Unix" 12 | fn_brace_style = "SameLineWhere" 13 | item_brace_style = "SameLineWhere" 14 | control_style = "Rfc" 15 | control_brace_style = "AlwaysSameLine" 16 | impl_empty_single_line = true 17 | trailing_comma = "Vertical" 18 | fn_empty_single_line = true 19 | fn_single_line = false 20 | fn_return_indent = "WithArgs" 21 | fn_args_paren_newline = false 22 | fn_args_density = "Tall" 23 | fn_args_layout = "Block" 24 | array_layout = "Block" 25 | array_width = 60 26 | type_punctuation_density = "Wide" 27 | where_style = "Rfc" 28 | where_density = "CompressedIfEmpty" 29 | where_layout = "Vertical" 30 | where_pred_indent = "Visual" 31 | generics_indent = "Block" 32 | struct_lit_style = "Block" 33 | struct_lit_multiline_style = "PreferSingle" 34 | fn_call_style = "Block" 35 | report_todo = "Never" 36 | report_fixme = "Never" 37 | chain_indent = "Block" 38 | chain_one_line_max = 60 39 | chain_split_single_child = false 40 | reorder_imports = true 41 | reorder_imports_in_group = true 42 | reorder_imported_names = true 43 | single_line_if_else_max_width = 50 44 | format_strings = false 45 | force_format_strings = false 46 | take_source_hints = false 47 | hard_tabs = false 48 | wrap_comments = false 49 | comment_width = 80 50 | normalize_comments = true 51 | wrap_match_arms = true 52 | match_block_trailing_comma = false 53 | indent_match_arms = true 54 | closure_block_indent_threshold = 7 55 | space_before_type_annotation = false 56 | space_after_type_annotation_colon = true 57 | space_before_struct_lit_field_colon = false 58 | space_after_struct_lit_field_colon = true 59 | space_before_bound = false 60 | space_after_bound_colon = true 61 | spaces_around_ranges = false 62 | spaces_within_angle_brackets = false 63 | spaces_within_square_brackets = false 64 | spaces_within_parens = false 65 | use_try_shorthand = true 66 | write_mode = "Replace" 67 | condense_wildcard_suffixes = false 68 | combine_control_expr = true 69 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | extern crate proc_macro; 3 | extern crate syn; 4 | #[macro_use] 5 | extern crate quote; 6 | 7 | use proc_macro::TokenStream; 8 | use quote::ToTokens; 9 | use syn::{Data, DeriveInput, Field, Fields, Variant}; 10 | 11 | #[proc_macro_derive(EnumError)] 12 | pub fn enum_error(input: TokenStream) -> TokenStream { 13 | let ast = into_ast(input); 14 | let froms = impl_into_enum(&ast); 15 | let display = impl_display(&ast); 16 | let error = impl_error(&ast); 17 | let tokens = quote!{ #froms #display #error }; 18 | tokens.into() 19 | } 20 | 21 | #[proc_macro_derive(IntoEnum)] 22 | pub fn into_enum(input: TokenStream) -> TokenStream { 23 | let ast = into_ast(input); 24 | let froms = impl_into_enum(&ast); 25 | let tokens = quote!{ #froms }; 26 | tokens.into() 27 | } 28 | 29 | #[proc_macro_derive(NewType)] 30 | pub fn newtype(input: TokenStream) -> TokenStream { 31 | let ast = into_ast(input); 32 | let from = impl_newtype_from(&ast); 33 | let deref = impl_newtype_deref(&ast); 34 | let tokens = quote!{ #from #deref }; 35 | tokens.into() 36 | } 37 | 38 | fn into_ast(input: TokenStream) -> DeriveInput { 39 | syn::parse(input).unwrap() 40 | } 41 | 42 | fn get_enum_variants(ast: &DeriveInput) -> Vec<&Variant> { 43 | match ast.data { 44 | Data::Enum(ref inner) => inner.variants.iter().collect(), 45 | Data::Struct(_) => unreachable!("Structs are not supported"), 46 | Data::Union(_) => unreachable!("Unions are not supported"), 47 | } 48 | } 49 | 50 | fn get_basetype(ast: &DeriveInput) -> &Field { 51 | match ast.data { 52 | Data::Enum(_) => unreachable!("Enums are not supported"), 53 | Data::Union(_) => unreachable!("Unions are not supported"), 54 | Data::Struct(ref ds) => { 55 | match ds.fields { 56 | Fields::Named(_) => unreachable!("Must be tuple struct"), 57 | Fields::Unnamed(ref t) => { 58 | if t.unnamed.len() > 1 { 59 | unreachable!("Must be one type"); 60 | } 61 | &t.unnamed[0] 62 | } 63 | Fields::Unit => unreachable!("Must be tuple struct"), 64 | } 65 | } 66 | } 67 | } 68 | 69 | fn impl_into_enum(ast: &DeriveInput) -> Box { 70 | let name = &ast.ident; 71 | let variants = get_enum_variants(&ast); 72 | let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); 73 | let impls = variants.iter().map(|var| { 74 | let v = &var.ident; 75 | let cont = match var.fields { 76 | Fields::Unnamed(ref c) => &c.unnamed, 77 | _ => unreachable!(), 78 | }; 79 | assert!(cont.len() == 1, "Single tuple is required"); 80 | let ctype = &cont[0].ty; 81 | quote!{ 82 | impl #impl_generics From<#ctype> for #name #ty_generics #where_clause { 83 | fn from(val: #ctype) -> Self { 84 | #name::#v(val) 85 | } 86 | } 87 | } 88 | }); 89 | Box::new(quote!{ #(#impls)* }) 90 | } 91 | 92 | fn impl_newtype_from(ast: &DeriveInput) -> Box { 93 | let name = &ast.ident; 94 | let field = get_basetype(&ast); 95 | let base = &field.ty; 96 | let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); 97 | 98 | Box::new( 99 | quote!{ 100 | impl #impl_generics From<#base> for #name #ty_generics #where_clause { 101 | fn from(val: #base) -> Self { 102 | #name(val) 103 | } 104 | } 105 | 106 | impl #impl_generics Into<#base> for #name #ty_generics #where_clause { 107 | fn into(self) -> #base { 108 | self.0 109 | } 110 | } 111 | } 112 | ) 113 | } 114 | 115 | fn impl_newtype_deref(ast: &DeriveInput) -> Box { 116 | let name = &ast.ident; 117 | let field = get_basetype(&ast); 118 | let base = &field.ty; 119 | let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); 120 | Box::new( 121 | quote!{ 122 | impl #impl_generics ::std::ops::Deref for #name #ty_generics #where_clause { 123 | type Target = #base; 124 | fn deref(&self) -> &Self::Target { &self.0 } 125 | } 126 | impl #impl_generics ::std::ops::DerefMut for #name #ty_generics #where_clause { 127 | fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } 128 | } 129 | } 130 | ) 131 | } 132 | 133 | fn impl_error(ast: &DeriveInput) -> Box { 134 | let name = &ast.ident; 135 | let variants = get_enum_variants(&ast); 136 | let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); 137 | let snips = variants.iter().map(|var| { 138 | let v = &var.ident; 139 | quote!{ #name::#v(ref err) => err.description() } 140 | }); 141 | Box::new( 142 | quote!{ 143 | impl #impl_generics ::std::error::Error for #name #ty_generics #where_clause { 144 | fn description(&self) -> &str { 145 | match *self { 146 | #(#snips), * 147 | } 148 | } 149 | } 150 | } 151 | ) 152 | } 153 | 154 | fn impl_display(ast: &DeriveInput) -> Box { 155 | let name = &ast.ident; 156 | let variants = get_enum_variants(&ast); 157 | let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); 158 | let snips = variants.iter().map(|var| { 159 | let v = &var.ident; 160 | quote!{ #name::#v(ref err) => err.fmt(f) } 161 | }); 162 | Box::new( 163 | quote!{ 164 | impl #impl_generics ::std::fmt::Display for #name #ty_generics #where_clause { 165 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 166 | match *self { 167 | #(#snips), * 168 | } 169 | } 170 | } 171 | } 172 | ) 173 | } 174 | -------------------------------------------------------------------------------- /tests/enum_error.rs: -------------------------------------------------------------------------------- 1 | 2 | #[macro_use] 3 | extern crate procedurals; 4 | 5 | use std::{fmt, io}; 6 | 7 | #[derive(Debug, EnumError)] 8 | pub enum Error { 9 | IO(io::Error), 10 | Fmt(fmt::Error), 11 | } 12 | 13 | #[test] 14 | fn test_all() { 15 | merged_error().unwrap(); 16 | } 17 | 18 | fn io_error() -> Result<(), io::Error> { 19 | Ok(()) 20 | } 21 | 22 | fn fmt_error() -> Result<(), fmt::Error> { 23 | Ok(()) 24 | } 25 | 26 | fn merged_error() -> Result<(), Error> { 27 | io_error()?; 28 | fmt_error()?; 29 | Ok(()) 30 | } 31 | -------------------------------------------------------------------------------- /tests/into_enum.rs: -------------------------------------------------------------------------------- 1 | 2 | #[macro_use] 3 | extern crate procedurals; 4 | 5 | struct A {} 6 | struct B {} 7 | 8 | #[derive(IntoEnum)] 9 | enum E { 10 | A(A), 11 | B(B), 12 | } 13 | 14 | #[test] 15 | fn into_enum() { 16 | let a = A {}; 17 | let _e: E = a.into(); 18 | } 19 | -------------------------------------------------------------------------------- /tests/newtype.rs: -------------------------------------------------------------------------------- 1 | 2 | #[macro_use] 3 | extern crate procedurals; 4 | 5 | pub struct Base { 6 | pub a: A, 7 | } 8 | 9 | #[derive(NewType)] 10 | pub struct New(Base); 11 | 12 | impl Base { 13 | fn new(a: A) -> Self { 14 | Self { a } 15 | } 16 | 17 | fn func(&self) { 18 | println!("This is B"); 19 | } 20 | 21 | fn func_mut(&mut self) { 22 | println!("Get mutable ref of B"); 23 | } 24 | } 25 | 26 | #[test] 27 | fn newtype() { 28 | let b: Base = Base::new(1); 29 | let mut n: New = b.into(); // test From 30 | n.func(); // test Deref 31 | n.func_mut(); // test DerefMut 32 | let _b: Base = n.into(); 33 | } 34 | --------------------------------------------------------------------------------