interpolate_name-0.2.4/.cargo_vcs_info.json0000644000000001360000000000100143650ustar { "git": { "sha1": "a4b0f8cf45654fdd400fab0d50677db34cea8a0a" }, "path_in_vcs": "" }interpolate_name-0.2.4/.gitignore000064400000000000000000000000361046102023000151440ustar 00000000000000/target **/*.rs.bk Cargo.lock interpolate_name-0.2.4/Cargo.lock0000644000000021400000000000100123350ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "interpolate_name" version = "0.2.4" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "proc-macro2" version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "syn" version = "2.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" interpolate_name-0.2.4/Cargo.toml0000644000000020200000000000100123550ustar # 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 are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "interpolate_name" version = "0.2.4" authors = ["Luca Barbato "] description = "Simple procedural macro attribute for repetitive tests" readme = "README.md" keywords = [ "proc_macro", "testing", ] categories = ["development-tools::testing"] license = "MIT" repository = "https://github.com/lu-zero/interpolate_name" [lib] test = false proc-macro = true [dependencies.proc-macro2] version = "1.0" [dependencies.quote] version = "1.0" [dependencies.syn] version = "2.0" features = [ "fold", "full", ] interpolate_name-0.2.4/Cargo.toml.orig000064400000000000000000000007761046102023000160560ustar 00000000000000[package] name = "interpolate_name" version = "0.2.4" authors = ["Luca Barbato "] description = "Simple procedural macro attribute for repetitive tests" repository = "https://github.com/lu-zero/interpolate_name" readme = "README.md" keywords = ["proc_macro", "testing"] categories = ["development-tools::testing"] license = "MIT" edition = "2018" [lib] proc-macro = true test = false [dependencies] syn = { version = "2.0", features = ["fold", "full"] } quote = "1.0" proc-macro2 = "1.0" interpolate_name-0.2.4/LICENSE000064400000000000000000000020551046102023000141640ustar 00000000000000MIT License Copyright (c) 2018 Luca Barbato Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. interpolate_name-0.2.4/README.md000064400000000000000000000006631046102023000144410ustar 00000000000000# Procedural macro attribute to do not repeat yourself while testing ## Usage ``` rust #[macro_use] extern crate interpolate_name; use interpolate_name::interpolate_test; #[interpolate_test(foo, "foo")] #[interpolate_test(bar, "bar")] #[interpolate_test(baz, "baz")] fn testme(f: &str) { println!("testing {}", f); } ``` Produces ``` running 3 tests test testme_baz ... ok test testme_bar ... ok test testme_foo ... ok ``` interpolate_name-0.2.4/examples/rename.rs000064400000000000000000000001521046102023000166060ustar 00000000000000use interpolate_name::interpolate_name; #[interpolate_name(one)] fn t() { } fn main() { t_one(); } interpolate_name-0.2.4/src/lib.rs000064400000000000000000000131641046102023000150650ustar 00000000000000//! # interpolate_name //! //! `interpolate_name` consists in a set of procedural macro attributes //! geared towards reduce the boilerplate while writing repetitive tests. //! //! - `interpolate_test`: a quick way to test the same function by passing specific //! arguments and have a test entry for each of them. //! - `interpolate_name`: a simple function renamer that can be combined //! with macros to support more complex patterns. extern crate proc_macro; use syn::{parse_macro_input, Attribute, ItemFn}; use proc_macro2::{Ident, Span, TokenStream, TokenTree}; use quote::{quote_spanned, ToTokens}; fn fn_name(item: TokenStream) -> Ident { let mut tokens = item.into_iter(); let found = tokens .find(|tok| { if let TokenTree::Ident(word) = tok { if word == "fn" { true } else { false } } else { false } }) .is_some(); if !found { panic!("the macro attribute applies only to functions") } match tokens.next() { Some(TokenTree::Ident(word)) => word, _ => panic!("failed to find function name"), } } fn fn_attrs_name(item: TokenStream) -> (TokenStream, Ident) { let mut tokens = item.into_iter(); let attrs = tokens .by_ref() .take_while(|tok| { if let TokenTree::Ident(word) = tok { if word == "fn" { false } else { true } } else { true } }) .collect::>(); let name = match tokens.next() { Some(TokenTree::Ident(word)) => word, _ => panic!("the macro attribute applies only to functions"), }; (TokenStream::from_iter(attrs.into_iter()), name) } /// Rename the decorated function by appending `_` and the provided `specifier`. /// /// ``` no_run /// use interpolate_name::interpolate_name; /// /// #[interpolate_name(spec)] /// fn foo() {} /// ``` /// /// produces: /// ``` no_run /// fn foo_spec() {} /// ``` #[proc_macro_attribute] pub fn interpolate_name( attr: proc_macro::TokenStream, item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { let tokens = TokenStream::from(attr).into_iter().collect::>(); if tokens.len() != 1 { panic!("expected #[interpolate_name(specifier)]"); } let specifier = match &tokens[0] { TokenTree::Literal(tt) => tt.to_string(), TokenTree::Ident(tt) => tt.to_string(), _ => panic!("expected #[interpolate_name(specifier)]"), }; let item = TokenStream::from(item); let (attrs, name) = fn_attrs_name(item.clone()); let interpolated_name = Ident::new( &format!("{}_{}", name.to_string(), specifier), Span::call_site(), ); let ret: TokenStream = quote_spanned! { proc_macro2::Span::call_site() => #attrs fn #interpolated_name() { #item return #name (); } } .into(); ret.into() } use std::iter::FromIterator; /// Generate a new test that calls the decorated function with the provided arguments. /// /// The test function name is the same as the called plus `_` and `specifier`. /// Can decorate the same function multiple times. /// /// ``` no_run /// use interpolate_name::interpolate_test; /// /// #[interpolate_test(some, "some", "arguments", 1)] /// #[interpolate_test(name, "other", "arguments", 42)] /// fn foo(a: &str, b: &str, c: usize) { /// println!("{} {} {}", a, b,c); /// } /// ``` /// /// produces: /// ``` no_run /// #[test] /// fn foo_some() { foo("some", "arguments", 1); } /// #[test] /// fn foo_name() { foo("other", "arguments", 42); } /// ``` #[proc_macro_attribute] pub fn interpolate_test( attr: proc_macro::TokenStream, item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { let tokens = TokenStream::from(attr).into_iter().collect::>(); if tokens.len() < 3 { panic!("expected #[interpolate_test(specifier, arguments)]"); } let specifier = match &tokens[0] { TokenTree::Literal(tt) => tt.to_string(), TokenTree::Ident(tt) => tt.to_string(), _ => panic!("expected #[interpolate_test(specifier, arguments)]"), }; match &tokens[1] { TokenTree::Punct(p) if p.as_char() == ',' => {} _ => panic!("expected #[interpolate_test(specifier, arguments)]"), } let args = TokenStream::from_iter(tokens.into_iter().skip(2)); let mut syn_item = parse_macro_input!(item as ItemFn); let (mut interpolate_attrs, attrs): (Vec, Vec) = syn_item.attrs.iter().cloned().partition(|attr| { let found = if let Some(seg) = attr.path().segments.last() { seg.ident == "interpolate_test" } else { false }; found }); let append_attrs = !interpolate_attrs.is_empty(); if append_attrs { interpolate_attrs.extend(attrs.iter().cloned()); } syn_item.attrs = interpolate_attrs; let attrs = TokenStream::from_iter(attrs.iter().map(|it| it.into_token_stream())); let item = TokenStream::from(syn_item.into_token_stream()); let name = fn_name(item.clone()); let interpolated_name = Ident::new( &format!("{}_{}", name.to_string(), specifier), Span::call_site(), ); let ret: TokenStream = quote_spanned! { proc_macro2::Span::call_site() => #item #[test] #attrs fn #interpolated_name() { return #name (#args); } } .into(); ret.into() } interpolate_name-0.2.4/tests/interpolate_name.rs000064400000000000000000000001671046102023000202170ustar 00000000000000use interpolate_name::interpolate_name; #[allow(unnameable_test_items)] #[interpolate_name(1)] #[test] fn t() { } interpolate_name-0.2.4/tests/interpolate_test.rs000064400000000000000000000012141046102023000202500ustar 00000000000000use interpolate_name::interpolate_test; #[interpolate_test(foo, "foo")] #[interpolate_test(bar, "bar")] #[interpolate_test(baz, "baz")] #[interpolate_test(124, "test_literal")] fn testme(f: &str) { println!("testing {}", f); } #[interpolate_test(foo, "foo")] #[interpolate_test(bar, "bar")] #[interpolate_test(baz, "baz")] #[interpolate_test(124, "test_literal")] #[ignore] fn testme_ignore(f: &str) { println!("testing {}", f); } #[cfg_attr(not(feature = "a"), interpolate_name::interpolate_test(a, "a"))] #[cfg_attr(feature = "b", interpolate_name::interpolate_test(b, "b"))] fn testme_variants(f: &str) { println!("testing {}", f); }