generic_new/lib.rs
1//! <div align="center">
2//!
3//! [](https://crates.io/crates/generic-new)
4//! [](https://docs.rs/generic-new)
5//! [](https://github.com/aatifsyed/generic-new)
6//!
7//! </div>
8//!
9//! A derive macro which generates an ergonomic constructor with shortcuts for certain types.
10//!
11//! ```rust
12//! # use std::path::PathBuf;
13//! use generic_new::GenericNew;
14//!
15//! #[derive(GenericNew)]
16//! struct Foo {
17//! s: String, // -> impl AsRef<str>
18//! v: Vec<usize>, // -> impl IntoIterator<Item = usize>
19//! i: Vec<String>, // -> impl IntoIterator<Item = impl AsRef<str>>
20//! p: PathBuf, // -> impl AsRef<Path>
21//! #[generic_new(ignore)]
22//! o: String, // Turn off magic conversion for some fields
23//! #[generic_new(ty = impl Into<usize>, converter = |u|Into::into(u))]
24//! u: usize, // Custom converters are supported
25//! }
26//!
27//! # fn _make_foo() {
28//! Foo::new(
29//! "hello",
30//! [1, 2, 3],
31//! ["a", "b", "c"],
32//! "path/to/foo",
33//! String::from("world"),
34//! 1u16,
35//! );
36//!
37//! # }
38//! ```
39
40use field::{make_field_configs, FieldConfig};
41use proc_macro::TokenStream;
42use proc_macro_error::{abort, proc_macro_error};
43use quote::quote;
44use syn::{parse_macro_input, DeriveInput};
45mod attributes;
46mod config;
47mod field;
48
49#[proc_macro_error]
50#[proc_macro_derive(GenericNew, attributes(generic_new))]
51pub fn derive_generic_new(input: TokenStream) -> TokenStream {
52 pretty_env_logger::try_init().ok();
53 let derive_input = parse_macro_input!(input as DeriveInput);
54 let user_ident = derive_input.ident.clone();
55
56 match derive_input.data {
57 syn::Data::Struct(ref user_struct) => {
58 let field_infos = make_field_configs(user_struct);
59 let inputs = field_infos.iter().map(FieldConfig::input);
60 let transforms = field_infos.iter().map(FieldConfig::transform);
61 let outputs = field_infos.iter().map(FieldConfig::output);
62
63 let constructor = match user_struct.fields {
64 syn::Fields::Named(_) => quote!(Self {#(#outputs,)*}),
65 syn::Fields::Unnamed(_) => quote!(Self(#(#outputs,)*)),
66 syn::Fields::Unit => abort!(derive_input, "Unit fields are not supported"),
67 };
68
69 let appended = quote! {
70 impl #user_ident {
71 pub fn new(
72 #(#inputs,)*
73 ) -> Self {
74 #(#transforms;)*
75 #constructor
76 }
77 }
78 };
79 appended.into()
80 }
81 syn::Data::Enum(_) => abort!(derive_input, "Enums are not yet supported"),
82 syn::Data::Union(_) => abort!(derive_input, "Unions are not supported"),
83 }
84}
85#[cfg(test)]
86mod tests {
87 #[test]
88 fn ui() {
89 let t = trybuild::TestCases::new();
90 t.pass("trybuild/pass/*.rs");
91 t.compile_fail("trybuild/fail/*.rs");
92 }
93}