Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#[builder(default)] now honors struct impls #62

Merged
merged 15 commits into from
Apr 8, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
sudo: false
language: rust
cache: cargo
# cache: cargo # blocked by https://github.com/brson/rust-skeptic/issues/18
rust:
- beta
- stable
- 1.15.0
matrix:
include:
- rust: 1.15.0
env: JOB=build CARGO_FEATURES="struct_default"
- rust: nightly
env: JOB=build CARGO_FEATURES="compiletests logging"
- rust: nightly
Expand Down
8 changes: 8 additions & 0 deletions derive_builder/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Deprecated
- `#[builder(default)]` and `#[builder(default="...")]` at the struct level will
change their behaviour in 0.5.0 and construct a default value for the struct,
instead of all fields individually. To opt into the new behaviour and squelch
this deprecation warning you can add the `struct_default` feature flag.

## [0.4.0] - 2017-03-25

### Added
Expand Down
5 changes: 3 additions & 2 deletions derive_builder/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "derive_builder"
version = "0.4.0"
version = "0.4.1"
authors = ["Colin Kiegel <[email protected]>",
"Pascal Hertleif <[email protected]>",
"Jan-Erik Rediger <[email protected]>"]
Expand All @@ -22,7 +22,8 @@ travis-ci = { repository = "colin-kiegel/rust-derive-builder" }
proc-macro = true

[features]
logging = ["log", "env_logger", "derive_builder_core/logging"]
logging = [ "log", "env_logger", "derive_builder_core/logging" ]
struct_default = []
skeptic_tests = ["skeptic"]

[dependencies]
Expand Down
1 change: 1 addition & 0 deletions derive_builder/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ with [cargo-edit](https://github.com/killercup/cargo-edit):
* **Setter visibility**: You can opt into private setter by preceding your struct with `#[builder(private)]`.
* **Setter type conversions**: With ``#[builder(setter(into))]`, setter methods will be generic over the input types – you can then supply every argument that implements the [`Into`][into] trait for the field type.
* **Generic structs**: Are also supported, but you **must not** use a type parameter named `VALUE`, if you also activate setter type conversions.
* **Default values**: You can use `#[builder(default)]` to delegate to the `Default` implementation or any explicit value via `=".."`. This works both on the struct and field level.
* **no_std support**: Just add `#[builder(no_std)]` to your struct and add `extern crate collections` to your crate. The latter requires the _nightly_ toolchain.
* **Logging**: If anything works unexpectedly you can enable detailed logs in two steps. First, add `features = ["logging"]` to the `derive_builder` dependency in `Cargo.toml`. Second, set this environment variable before calling cargo `RUST_LOG=derive_builder=trace`.

Expand Down
2 changes: 1 addition & 1 deletion derive_builder/src/doc_tpl/builder_method.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ Builds a new `{struct_name}`.

# Errors

If some field has not been initialized.
If a required field has not been initialized.
11 changes: 10 additions & 1 deletion derive_builder/src/options/field_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub struct FieldMode {
field_type: syn::Ty,
setter_attrs: Option<Vec<syn::Attribute>>,
deprecation_notes: DeprecationNotes,
pub use_default_struct: bool,
}

impl Default for FieldMode {
Expand All @@ -17,6 +18,7 @@ impl Default for FieldMode {
field_type: syn::Ty::Never,
setter_attrs: None,
deprecation_notes: Default::default(),
use_default_struct: false,
}
}
}
Expand All @@ -32,6 +34,7 @@ impl OptionsBuilder<FieldMode> {
field_type: f.ty,
setter_attrs: None,
deprecation_notes: Default::default(),
use_default_struct: false,
});

builder.parse_attributes(&f.attrs);
Expand All @@ -55,7 +58,7 @@ impl OptionsBuilder<FieldMode> {
let mut deprecation_notes = self.mode.deprecation_notes;
deprecation_notes.extend(&defaults.mode.deprecation_notes);

/// move a nested field out of `self`, if it `Some(_)` or else clone it from `defaults`
// move a nested field out of `self`, if it is `Some(_)` or else clone it from `defaults`
macro_rules! f {
($($field:ident).*) => {
self.$($field).*.or_else(|| defaults.$($field).*.clone())
Expand All @@ -67,6 +70,7 @@ impl OptionsBuilder<FieldMode> {
field_type: self.mode.field_type,
setter_attrs: f!(mode.setter_attrs),
deprecation_notes: deprecation_notes,
use_default_struct: self.mode.use_default_struct || defaults.mode.use_default_struct,
};

OptionsBuilder::<FieldMode> {
Expand Down Expand Up @@ -103,6 +107,10 @@ impl OptionsBuilderMode for FieldMode {
panic!("Support for `#![no_std]` can only be set on the stuct level (but found {}).",
self.where_diagnostics())
}

fn struct_mode(&self) -> bool {
false
}
}

impl From<OptionsBuilder<FieldMode>> for FieldOptions {
Expand Down Expand Up @@ -131,6 +139,7 @@ impl From<OptionsBuilder<FieldMode>> for FieldOptions {
setter_into: b.setter_into.unwrap_or(false),
deprecation_notes: b.mode.deprecation_notes.clone(),
default_expression: b.default_expression.clone(),
use_default_struct: b.mode.use_default_struct,
attrs: b.mode.setter_attrs.clone().unwrap_or_default(),
}
}
Expand Down
40 changes: 22 additions & 18 deletions derive_builder/src/options/field_options.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use syn;
use derive_builder_core::{DeprecationNotes, BuilderPattern, Setter, Initializer, BuilderField};
use derive_builder_core::{DeprecationNotes, BuilderPattern, Setter, Initializer, BuilderField,
Block};
use options::DefaultExpression;

/// These field options define how the builder interacts with the field.
#[derive(Debug, Clone)]
Expand All @@ -12,8 +14,10 @@ pub struct FieldOptions {
pub setter_ident: syn::Ident,
/// Visibility of the setter, e.g. `syn::Visibility::Public`.
pub setter_visibility: syn::Visibility,
/// e.g. `#[builder(default="42u32")]` (default to None)
/// Default expression for the field, e.g. `#[builder(default="42u32")]` (default to None).
pub default_expression: Option<DefaultExpression>,
/// Whether the build_method defines a default struct.
pub use_default_struct: bool,
/// The field name, may deviate from `setter_ident`.
pub field_ident: syn::Ident,
/// The field type.
Expand All @@ -26,11 +30,20 @@ pub struct FieldOptions {
pub attrs: Vec<syn::Attribute>
}

/// A `DefaultExpression` can be either explicit or refer to the canonical trait.
#[derive(Debug, Clone)]
pub enum DefaultExpression {
Explicit(String),
Trait,
impl DefaultExpression {
pub fn parse_block(&self) -> Block {
let expr = match *self {
DefaultExpression::Explicit(ref s) => {
if s.is_empty() {
panic!(r#"Empty default expressions `default=""` are not supported."#);
}
s
},
DefaultExpression::Trait => "::std::default::Default::default()",
};

expr.parse().expect(&format!("Couldn't parse default expression `{:?}`", self))
}
}

impl FieldOptions {
Expand Down Expand Up @@ -59,17 +72,8 @@ impl FieldOptions {
setter_enabled: self.setter_enabled,
field_ident: &self.field_ident,
builder_pattern: self.builder_pattern,
explicit_default: self.default_expression.as_ref().map(|x| {
match *x {
DefaultExpression::Explicit(ref s) => {
if s.is_empty() {
panic!(r#"Empty default expressions `default=""` are not supported."#);
}
s
},
DefaultExpression::Trait => "::std::default::Default::default()",
}.parse().expect(&format!("Couldn't parse default expression `{:?}`", x))
}),
default_value: self.default_expression.as_ref().map(|x| { x.parse_block() }),
use_default_struct: self.use_default_struct,
}
}

Expand Down
18 changes: 17 additions & 1 deletion derive_builder/src/options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ pub use self::field_mode::FieldMode;
pub use self::field_options::FieldOptions;
pub use self::struct_mode::StructMode;
pub use self::struct_options::StructOptions;
use self::field_options::DefaultExpression;

/// A `DefaultExpression` can be either explicit or refer to the canonical trait.
#[derive(Debug, Clone)]
pub enum DefaultExpression {
Explicit(String),
Trait,
}

/// Get the tuple of `StructOptions` and field defaults (`OptionsBuilder<FieldMode>`) from the AST.
pub fn struct_options_from(ast: &syn::MacroInput) -> (StructOptions, OptionsBuilder<FieldMode>) {
Expand Down Expand Up @@ -49,6 +55,7 @@ pub trait OptionsBuilderMode: ::std::fmt::Debug {
/// Provide a diagnostic _where_-clause for panics.
fn where_diagnostics(&self) -> String;
fn no_std(&mut self, x: bool);
fn struct_mode(&self) -> bool;
}

impl<Mode> From<Mode> for OptionsBuilder<Mode> {
Expand Down Expand Up @@ -185,6 +192,15 @@ impl<Mode> OptionsBuilder<Mode> where
self.setter_enabled(true)
},
"default" => {
if !cfg!(feature = "struct_default") && self.mode.struct_mode() {
let where_info = self.where_diagnostics();
self.mode.push_deprecation_note(format!(
"the meaning of `#[builder(default)]` on the struct level (found {}) will change in the \
next version (see https://github.com/colin-kiegel/rust-derive-builder/issues/61 for \
more details). To squelch this message and adopt the new behavior now, compile \
`derive_builder` with `--features \"struct_default\"`.", where_info));
}

self.default_expression(DefaultExpression::Trait)
},
"no_std" => {
Expand Down
18 changes: 16 additions & 2 deletions derive_builder/src/options/struct_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,32 @@ impl OptionsBuilderMode for StructMode {
desc: "no_std support",
map: |x: bool| { x },
}

fn struct_mode(&self) -> bool {
true
}
}

impl From<OptionsBuilder<StructMode>> for (StructOptions, OptionsBuilder<FieldMode>) {
fn from(b: OptionsBuilder<StructMode>) -> (StructOptions, OptionsBuilder<FieldMode>) {
#[cfg(feature = "struct_default")]
let (field_default_expression, struct_default_expression) = (None, b.default_expression);
#[cfg(not(feature = "struct_default"))]
let (field_default_expression, struct_default_expression) = (b.default_expression, None);

let field_defaults = OptionsBuilder::<FieldMode> {
setter_enabled: b.setter_enabled,
builder_pattern: b.builder_pattern,
setter_name: None,
setter_prefix: b.setter_prefix,
setter_vis: b.setter_vis,
setter_into: b.setter_into,
default_expression: b.default_expression,
mode: FieldMode::default(),
default_expression: field_default_expression,
mode: {
let mut mode = FieldMode::default();
mode.use_default_struct = struct_default_expression.is_some();
mode
},
};

let m = b.mode;
Expand All @@ -94,6 +107,7 @@ impl From<OptionsBuilder<StructMode>> for (StructOptions, OptionsBuilder<FieldMo
generics: m.build_target_generics,
struct_size_hint: m.struct_size_hint,
no_std: m.no_std.unwrap_or(false),
default_expression: struct_default_expression,
};

(struct_options, field_defaults)
Expand Down
6 changes: 5 additions & 1 deletion derive_builder/src/options/struct_options.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use syn;
use derive_builder_core::{DeprecationNotes, BuilderPattern, Builder, BuildMethod};
use options::DefaultExpression;

/// These struct options define how the builder is generated.
#[derive(Debug, Clone)]
Expand All @@ -23,6 +24,8 @@ pub struct StructOptions {
pub struct_size_hint: usize,
/// Whether the generated code should comply with `#![no_std]`.
pub no_std: bool,
/// Default expression for the whole struct, e.g. `#[builder(default)]` (default to None).
pub default_expression: Option<DefaultExpression>,
}

impl StructOptions {
Expand All @@ -36,7 +39,7 @@ impl StructOptions {
fields: Vec::with_capacity(self.struct_size_hint),
functions: Vec::with_capacity(self.struct_size_hint),
doc_comment: None,
deprecation_notes: DeprecationNotes::default(),
deprecation_notes: self.deprecation_notes.clone(),
}
}
/// Returns a `BuildMethod` according to the options.
Expand All @@ -52,6 +55,7 @@ impl StructOptions {
initializers: Vec::with_capacity(self.struct_size_hint),
doc_comment: None,
no_std: self.no_std,
default_struct: self.default_expression.as_ref().map(|x| { x.parse_block() }),
}
}
}
Loading