derive-new-0.5.8/.gitignore010064400007650000024000000000101336736756000140070ustar0000000000000000target/ derive-new-0.5.8/.travis.yml010064400007650000024000000005461352613425300141320ustar0000000000000000sudo: false language: rust matrix: include: - rust: 1.31.0 - rust: stable - rust: beta - rust: nightly script: - cargo test --verbose - rm target/debug/deps/libderive_new-*.so - cargo test --verbose --manifest-path testcrate/Cargo.toml env: global: - RUST_BACKTRACE=1 script: - cargo test --verbose derive-new-0.5.8/Cargo.toml.orig010064400007650000024000000006401352613425300147030ustar0000000000000000[package] name = "derive-new" version = "0.5.8" authors = ["Nick Cameron "] description = "`#[derive(new)]` implements simple constructor functions for structs and enums." license = "MIT" repository = "https://github.com/nrc/derive-new" [lib] proc-macro = true [dependencies] proc-macro2 = "1" quote = "1" syn = "1" [features] default = ["std"] std = [] [workspace] members = ["testcrate"] derive-new-0.5.8/Cargo.toml0000644000000016760000000000000111620ustar00# 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] name = "derive-new" version = "0.5.8" authors = ["Nick Cameron "] description = "`#[derive(new)]` implements simple constructor functions for structs and enums." license = "MIT" repository = "https://github.com/nrc/derive-new" [lib] proc-macro = true [dependencies.proc-macro2] version = "1" [dependencies.quote] version = "1" [dependencies.syn] version = "1" [features] default = ["std"] std = [] derive-new-0.5.8/Cargo.toml.orig0000644000000016770000000000000121220ustar00# 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] name = "derive-new" version = "0.5.8" authors = ["Nick Cameron "] description = "`#[derive(new)]` implements simple constructor functions for structs and enums." license = "MIT" repository = "https://github.com/nrc/derive-new" [lib] proc-macro = true [dependencies.proc-macro2] version = "1" [dependencies.quote] version = "1" [dependencies.syn] version = "1" [features] default = ["std"] std = [] derive-new-0.5.8/README.md010064400007650000024000000042451337207665300133100ustar0000000000000000# A custom derive implementation for `#[derive(new)]` A `derive(new)` attribute creates a `new` constructor function for the annotated type. That function takes an argument for each field in the type giving a trivial constructor. This is useful since as your type evolves you can make the constructor non-trivial (and add or remove fields) without changing client code (i.e., without breaking backwards compatibility). It is also the most succinct way to initialise a struct or an enum. Implementation uses macros 1.1 custom derive (which works in stable Rust from 1.15 onwards). `#[no_std]` is fully supported if you switch off the default feature `"std"`. ## Examples Cargo.toml: ```toml [dependencies] derive-new = "0.5" ``` Include the macro: ```rust #[macro_use] extern crate derive_new; ``` Generating constructor for a simple struct: ```rust #[derive(new)] struct Bar { a: i32, b: String, } let _ = Bar::new(42, "Hello".to_owned()); ``` Default values can be specified either via `#[new(default)]` attribute which removes the argument from the constructor and populates the field with `Default::default()`, or via `#[new(value = "..")]` which initializes the field with a given expression: ```rust #[derive(new)] struct Foo { x: bool, #[new(value = "42")] y: i32, #[new(default)] z: Vec, } let _ = Foo::new(true); ``` Generic types are supported; in particular, `PhantomData` fields will be not included in the argument list and will be intialized automatically: ```rust use std::marker::PhantomData; #[derive(new)] struct Generic<'a, T: Default, P> { x: &'a str, y: PhantomData

, #[new(default)] z: T, } let _ = Generic::::new("Hello"); ``` For enums, one constructor method is generated for each variant, with the type name being converted to snake case; otherwise, all features supported for structs work for enum variants as well: ```rust #[derive(new)] struct Enum { FirstVariant, SecondVariant(bool, #[new(default)] u8), ThirdVariant { x: i32, #[new(value = "vec![1]")] y: Vec } } let _ = Enum::new_first_variant(); let _ = Enum::new_second_variant(true); let _ = Enum::new_third_variant(42); ``` derive-new-0.5.8/src/lib.rs010064400007650000024000000336061352613425300137270ustar0000000000000000//!# A custom derive implementation for `#[derive(new)]` //! //!A `derive(new)` attribute creates a `new` constructor function for the annotated //!type. That function takes an argument for each field in the type giving a //!trivial constructor. This is useful since as your type evolves you can make the //!constructor non-trivial (and add or remove fields) without changing client code //!(i.e., without breaking backwards compatibility). It is also the most succinct //!way to initialise a struct or an enum. //! //!Implementation uses macros 1.1 custom derive (which works in stable Rust from //!1.15 onwards). //! //!## Examples //! //!Cargo.toml: //! //!```toml //![dependencies] //!derive-new = "0.5" //!``` //! //!Include the macro: //! //!```rust //!#[macro_use] //!extern crate derive_new; //!fn main() {} //!``` //! //!Generating constructor for a simple struct: //! //!```rust //!#[macro_use] //!extern crate derive_new; //!#[derive(new)] //!struct Bar { //! a: i32, //! b: String, //!} //! //!fn main() { //! let _ = Bar::new(42, "Hello".to_owned()); //!} //!``` //! //!Default values can be specified either via `#[new(default)]` attribute which removes //!the argument from the constructor and populates the field with `Default::default()`, //!or via `#[new(value = "..")]` which initializes the field with a given expression: //! //!```rust //!#[macro_use] //!extern crate derive_new; //!#[derive(new)] //!struct Foo { //! x: bool, //! #[new(value = "42")] //! y: i32, //! #[new(default)] //! z: Vec, //!} //! //!fn main() { //! let _ = Foo::new(true); //!} //!``` //! //!Generic types are supported; in particular, `PhantomData` fields will be not //!included in the argument list and will be intialized automatically: //! //!```rust //!#[macro_use] //!extern crate derive_new; //!use std::marker::PhantomData; //! //!#[derive(new)] //!struct Generic<'a, T: Default, P> { //! x: &'a str, //! y: PhantomData

, //! #[new(default)] //! z: T, //!} //! //!fn main() { //! let _ = Generic::::new("Hello"); //!} //!``` //! //!For enums, one constructor method is generated for each variant, with the type //!name being converted to snake case; otherwise, all features supported for //!structs work for enum variants as well: //! //!```rust //!#[macro_use] //!extern crate derive_new; //!#[derive(new)] //!enum Enum { //! FirstVariant, //! SecondVariant(bool, #[new(default)] u8), //! ThirdVariant { x: i32, #[new(value = "vec![1]")] y: Vec } //!} //! //!fn main() { //! let _ = Enum::new_first_variant(); //! let _ = Enum::new_second_variant(true); //! let _ = Enum::new_third_variant(42); //!} //!``` #![crate_type = "proc-macro"] #![recursion_limit = "192"] extern crate proc_macro; extern crate proc_macro2; #[macro_use] extern crate quote; extern crate syn; macro_rules! my_quote { ($($t:tt)*) => (quote_spanned!(proc_macro2::Span::call_site() => $($t)*)) } fn path_to_string(path: &syn::Path) -> String { path.segments.iter().map(|s| s.ident.to_string()).collect::>().join("::") } use proc_macro::TokenStream; use syn::Token; #[proc_macro_derive(new, attributes(new))] pub fn derive(input: TokenStream) -> TokenStream { let ast: syn::DeriveInput = syn::parse(input).expect("Couldn't parse item"); let result = match ast.data { syn::Data::Enum(ref e) => new_for_enum(&ast, e), syn::Data::Struct(ref s) => new_for_struct(&ast, &s.fields, None), syn::Data::Union(_) => panic!("doesn't work with unions yet"), }; result.into() } fn new_for_struct( ast: &syn::DeriveInput, fields: &syn::Fields, variant: Option<&syn::Ident>, ) -> proc_macro2::TokenStream { match *fields { syn::Fields::Named(ref fields) => new_impl(&ast, Some(&fields.named), true, variant), syn::Fields::Unit => new_impl(&ast, None, false, variant), syn::Fields::Unnamed(ref fields) => new_impl(&ast, Some(&fields.unnamed), false, variant), } } fn new_for_enum(ast: &syn::DeriveInput, data: &syn::DataEnum) -> proc_macro2::TokenStream { if data.variants.is_empty() { panic!("#[derive(new)] cannot be implemented for enums with zero variants"); } let impls = data.variants.iter().map(|v| { if v.discriminant.is_some() { panic!("#[derive(new)] cannot be implemented for enums with discriminants"); } new_for_struct(ast, &v.fields, Some(&v.ident)) }); my_quote!(#(#impls)*) } fn new_impl( ast: &syn::DeriveInput, fields: Option<&syn::punctuated::Punctuated>, named: bool, variant: Option<&syn::Ident>, ) -> proc_macro2::TokenStream { let name = &ast.ident; let unit = fields.is_none(); let empty = Default::default(); let fields: Vec<_> = fields .unwrap_or(&empty) .iter() .enumerate() .map(|(i, f)| FieldExt::new(f, i, named)) .collect(); let args = fields.iter().filter(|f| f.needs_arg()).map(|f| f.as_arg()); let inits = fields.iter().map(|f| f.as_init()); let inits = if unit { my_quote!() } else if named { my_quote![{ #(#inits),* }] } else { my_quote![( #(#inits),* )] }; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let (mut new, qual, doc) = match variant { None => ( syn::Ident::new("new", proc_macro2::Span::call_site()), my_quote!(), format!("Constructs a new `{}`.", name), ), Some(ref variant) => ( syn::Ident::new( &format!("new_{}", to_snake_case(&variant.to_string())), proc_macro2::Span::call_site(), ), my_quote!(::#variant), format!("Constructs a new `{}::{}`.", name, variant), ), }; new.set_span(proc_macro2::Span::call_site()); let lint_attrs = collect_parent_lint_attrs(&ast.attrs); let lint_attrs = my_quote![#(#lint_attrs),*]; my_quote! { impl #impl_generics #name #ty_generics #where_clause { #[doc = #doc] #lint_attrs pub fn #new(#(#args),*) -> Self { #name #qual #inits } } } } fn collect_parent_lint_attrs(attrs: &[syn::Attribute]) -> Vec { fn is_lint(item: &syn::Meta) -> bool { if let syn::Meta::List(ref l) = *item { let path = &l.path; return path.is_ident("allow") || path.is_ident("deny") || path.is_ident("forbid") || path.is_ident("warn") } false } fn is_cfg_attr_lint(item: &syn::Meta) -> bool { if let syn::Meta::List(ref l) = *item { if l.path.is_ident("cfg_attr") && l.nested.len() == 2 { if let syn::NestedMeta::Meta(ref item) = l.nested[1] { return is_lint(item); } } } false } attrs .iter() .filter_map(|a| a.parse_meta().ok().map(|m| (m, a))) .filter(|&(ref m, _)| is_lint(m) || is_cfg_attr_lint(m)) .map(|p| p.1) .cloned() .collect() } enum FieldAttr { Default, Value(proc_macro2::TokenStream), } impl FieldAttr { pub fn as_tokens(&self) -> proc_macro2::TokenStream { match *self { FieldAttr::Default => { if cfg!(feature = "std") { my_quote!(::std::default::Default::default()) } else { my_quote!(::core::default::Default::default()) } } FieldAttr::Value(ref s) => my_quote!(#s), } } pub fn parse(attrs: &[syn::Attribute]) -> Option { use syn::{AttrStyle, Meta, NestedMeta}; let mut result = None; for attr in attrs.iter() { match attr.style { AttrStyle::Outer => {} _ => continue, } let last_attr_path = attr .path .segments .iter() .last() .expect("Expected at least one segment where #[segment[::segment*](..)]"); if (*last_attr_path).ident != "new" { continue; } let meta = match attr.parse_meta() { Ok(meta) => meta, Err(_) => continue, }; let list = match meta { Meta::List(l) => l, _ if meta.path().is_ident("new") => { panic!("Invalid #[new] attribute, expected #[new(..)]") } _ => continue, }; if result.is_some() { panic!("Expected at most one #[new] attribute"); } for item in list.nested.iter() { match *item { NestedMeta::Meta(Meta::Path(ref path)) => { if path.is_ident("default") { result = Some(FieldAttr::Default); } else { panic!("Invalid #[new] attribute: #[new({})]", path_to_string(&path)); } } NestedMeta::Meta(Meta::NameValue(ref kv)) => { if let syn::Lit::Str(ref s) = kv.lit { if kv.path.is_ident("value") { let tokens = s.value().parse().ok().expect(&format!( "Invalid expression in #[new]: `{}`", s.value() )); result = Some(FieldAttr::Value(tokens)); } else { panic!("Invalid #[new] attribute: #[new({} = ..)]", path_to_string(&kv.path)); } } else { panic!("Non-string literal value in #[new] attribute"); } } NestedMeta::Meta(Meta::List(ref l)) => { panic!("Invalid #[new] attribute: #[new({}(..))]", path_to_string(&l.path)); } NestedMeta::Lit(_) => { panic!("Invalid #[new] attribute: literal value in #[new(..)]"); } } } } result } } struct FieldExt<'a> { ty: &'a syn::Type, attr: Option, ident: syn::Ident, named: bool, } impl<'a> FieldExt<'a> { pub fn new(field: &'a syn::Field, idx: usize, named: bool) -> FieldExt<'a> { FieldExt { ty: &field.ty, attr: FieldAttr::parse(&field.attrs), ident: if named { field.ident.clone().unwrap() } else { syn::Ident::new(&format!("f{}", idx), proc_macro2::Span::call_site()) }, named: named, } } pub fn has_attr(&self) -> bool { self.attr.is_some() } pub fn is_phantom_data(&self) -> bool { match *self.ty { syn::Type::Path(syn::TypePath { qself: None, ref path, }) => path .segments .last() .map(|x| x.ident == "PhantomData") .unwrap_or(false), _ => false, } } pub fn needs_arg(&self) -> bool { !self.has_attr() && !self.is_phantom_data() } pub fn as_arg(&self) -> proc_macro2::TokenStream { let f_name = &self.ident; let ty = &self.ty; my_quote!(#f_name: #ty) } pub fn as_init(&self) -> proc_macro2::TokenStream { let f_name = &self.ident; let init = if self.is_phantom_data() { if cfg!(feature = "std") { my_quote!(::std::marker::PhantomData) } else { my_quote!(::core::marker::PhantomData) } } else { match self.attr { None => my_quote!(#f_name), Some(ref attr) => attr.as_tokens(), } }; if self.named { my_quote!(#f_name: #init) } else { my_quote!(#init) } } } fn to_snake_case(s: &str) -> String { let (ch, next, mut acc): (Option, Option, String) = s.chars() .fold((None, None, String::new()), |(prev, ch, mut acc), next| { if let Some(ch) = ch { if let Some(prev) = prev { if ch.is_uppercase() { if prev.is_lowercase() || prev.is_numeric() || (prev.is_uppercase() && next.is_lowercase()) { acc.push('_'); } } } acc.extend(ch.to_lowercase()); } (ch, Some(next), acc) }); if let Some(next) = next { if let Some(ch) = ch { if (ch.is_lowercase() || ch.is_numeric()) && next.is_uppercase() { acc.push('_'); } } acc.extend(next.to_lowercase()); } acc } #[test] fn test_to_snake_case() { assert_eq!(to_snake_case(""), ""); assert_eq!(to_snake_case("a"), "a"); assert_eq!(to_snake_case("B"), "b"); assert_eq!(to_snake_case("BC"), "bc"); assert_eq!(to_snake_case("Bc"), "bc"); assert_eq!(to_snake_case("bC"), "b_c"); assert_eq!(to_snake_case("Fred"), "fred"); assert_eq!(to_snake_case("CARGO"), "cargo"); assert_eq!(to_snake_case("_Hello"), "_hello"); assert_eq!(to_snake_case("QuxBaz"), "qux_baz"); assert_eq!(to_snake_case("FreeBSD"), "free_bsd"); assert_eq!(to_snake_case("specialK"), "special_k"); assert_eq!(to_snake_case("hello1World"), "hello1_world"); assert_eq!(to_snake_case("Keep_underscore"), "keep_underscore"); assert_eq!(to_snake_case("ThisISNotADrill"), "this_is_not_a_drill"); } derive-new-0.5.8/tests/test.rs010064400007650000024000000162551351171746700145240ustar0000000000000000#![deny(non_snake_case)] #[macro_use] extern crate derive_new; use std::default::Default; use std::fmt::Debug; use std::marker::PhantomData; /// A struct with no fields. #[derive(new, PartialEq, Debug)] pub struct Foo {} #[test] fn test_empty_struct() { let x = Foo::new(); assert_eq!(x, Foo {}); } /// A unit struct. #[derive(new, PartialEq, Debug)] pub struct Baz; #[test] fn test_unit_struct() { let x = Baz::new(); assert_eq!(x, Baz); } /// A struct with fields. #[derive(new, PartialEq, Debug)] pub struct Bar { pub x: i32, pub y: String, } #[test] fn test_simple_struct() { let x = Bar::new(42, "Hello".to_owned()); assert_eq!( x, Bar { x: 42, y: "Hello".to_owned() } ); } /// A struct with a lifetime parameter. #[derive(new, PartialEq, Debug)] pub struct Intersection<'scene> { pub object: &'scene Bar, pub normal: Foo, pub point: Foo, pub t: f64, } #[test] fn test_struct_with_lifetime() { let b = Bar::new(42, "Hello".to_owned()); let x = Intersection::new(&b, Foo::new(), Foo::new(), 42.0); assert_eq!( x, Intersection { object: &b, normal: Foo {}, point: Foo {}, t: 42.0 } ); } /// A struct with generics and bounds. #[derive(new, PartialEq, Debug)] pub struct Qux { pub f1: T, pub f2: Vec, pub f3: i32, } #[test] fn test_struct_with_bounds() { let x = Qux::new("Hello!", Vec::::new(), 42); assert_eq!( x, Qux { f1: "Hello!", f2: vec![], f3: 42 } ); let x: Qux<&'static str, String> = Qux::new("Hello!", Vec::::new(), 42); assert_eq!( x, Qux { f1: "Hello!", f2: vec![], f3: 42 } ); let x = Qux::<_, String>::new("Hello!", vec![], 42); assert_eq!( x, Qux { f1: "Hello!", f2: vec![], f3: 42 } ); } /// A struct with a lifetime parameter, generics and bounds. #[derive(new, PartialEq, Debug)] pub struct FooBar<'a, T, U> where T: 'a + PartialEq + Debug, U: Sized + Send + 'a + PartialEq + Debug, { pub f1: Box, pub f2: Vec<&'a U>, pub f3: i32, } #[test] fn test_struct_lifetime_bounds() { let a = 42; let x = FooBar::new(Box::new("Hello".to_owned()), vec![&a], 42); assert_eq!( x, FooBar { f1: Box::new("Hello".to_owned()), f2: vec![&a], f3: 42 } ); } /// A tuple struct. #[derive(new, PartialEq, Debug)] pub struct Tuple(pub i32, pub i32); #[test] fn test_simple_tuple_struct() { let x = Tuple::new(5, 6); assert_eq!(x, Tuple(5, 6)); } /// A tuple struct with a lifetime parameter. #[derive(new, PartialEq, Debug)] pub struct TupleWithLifetime<'a>(pub &'a str); #[test] fn test_tuple_struct_lifetime() { let x = TupleWithLifetime::new("Hello"); assert_eq!(x, TupleWithLifetime("Hello")); } /// A struct where fields have default values. #[derive(new, PartialEq, Debug)] pub struct Waldo { #[new(default)] pub x: i32, pub y: u8, #[new(default)] pub z: T, } #[test] fn test_struct_with_defaults() { let x = Waldo::>::new(42); assert_eq!( x, Waldo { x: 0, y: 42, z: vec![] } ); } /// A struct where fields have explicitly provided defaults. #[derive(new, PartialEq, Debug)] pub struct Fred { #[new(value = "1 + 2")] pub x: i32, pub y: String, #[new(value = "vec![-42, 42]")] pub z: Vec, } #[test] fn test_struct_with_values() { let x = Fred::new("Fred".to_owned()); assert_eq!( x, Fred { x: 3, y: "Fred".to_owned(), z: vec![-42, 42] } ); } /// A struct with defaults and specified values. #[derive(new, PartialEq, Debug)] pub struct Thud { #[new(value = r#""Thud".to_owned()"#)] pub x: String, #[new(default)] pub y: String, } #[test] fn test_struct_mixed_defaults() { let x = Thud::new(); assert_eq!( x, Thud { x: "Thud".to_owned(), y: String::new() } ); } /// A generic struct with PhantomData member. #[derive(new, PartialEq, Debug)] pub struct Bob { pub a: i32, pub b: PhantomData, } #[test] fn test_struct_phantom_data() { let x = Bob::::new(42); assert_eq!( x, Bob { a: 42, b: PhantomData } ); } /// A tuple struct where fields have default values. #[derive(new, PartialEq, Debug)] pub struct Boom( #[new(default)] pub i32, pub u8, #[new(default)] pub T, ); #[test] fn test_tuple_with_defaults() { let x = Boom::>::new(42); assert_eq!(x, Boom(0, 42, vec![])); } /// A tuple struct where fields have explicitly provided defaults. #[derive(new, PartialEq, Debug)] pub struct Moog( #[new(value = "1 + 2")] pub i32, pub String, #[new(value = "vec![-42, 42]")] pub Vec, ); #[test] fn test_tuple_with_values() { let x = Moog::new("Fred".to_owned()); assert_eq!(x, Moog(3, "Fred".to_owned(), vec![-42, 42])); } /// A tuple struct with defaults and specified values. #[derive(new, PartialEq, Debug)] pub struct Crab( #[new(value = r#""Thud".to_owned()"#)] pub String, #[new(default)] pub String, ); #[test] fn test_tuple_mixed_defaults() { let x = Crab::new(); assert_eq!(x, Crab("Thud".to_owned(), String::new())); } /// A generic tuple struct with PhantomData member. #[derive(new, PartialEq, Debug)] pub struct Sponge(pub i32, pub PhantomData); #[test] fn test_tuple_phantom_data() { let x = Sponge::::new(42); assert_eq!(x, Sponge(42, PhantomData)); } /// An enum with unit variants #[derive(new, PartialEq, Debug)] pub enum Fizz { ThisISNotADrill, BiteMe, } #[test] fn test_enum_unit_variants() { let x = Fizz::new_this_is_not_a_drill(); assert_eq!(x, Fizz::ThisISNotADrill); let x = Fizz::new_bite_me(); assert_eq!(x, Fizz::BiteMe); } /// A more involved enum #[derive(new, PartialEq, Debug)] pub enum Enterprise { Picard, Data( #[new(value = "\"fascinating\".to_owned()")] String, #[new(default)] T, ), Spock { x: PhantomData, y: i32, }, } #[test] fn test_more_involved_enum() { let x = Enterprise::::new_picard(); assert_eq!(x, Enterprise::Picard); let x = Enterprise::::new_data(); assert_eq!(x, Enterprise::Data("fascinating".to_owned(), 0u8)); let x = Enterprise::::new_spock(42); assert_eq!( x, Enterprise::Spock { x: PhantomData, y: 42 } ); } #[allow(non_snake_case)] #[derive(new, PartialEq, Debug)] pub struct Upside { X: i32, } #[cfg_attr(test, allow(non_snake_case))] #[derive(new, PartialEq, Debug)] pub struct Down { X: i32, } #[derive(new, PartialEq, Debug)] pub struct All { #[allow(missing_docs)] pub x: i32, } derive-new-0.5.8/.cargo_vcs_info.json0000644000000001120000000000000131440ustar00{ "git": { "sha1": "992b15ee7298d8a6a4bf1fedab14c96d712fea86" } }