Skip to content

Commit

Permalink
Merge pull request #306 from colin-kiegel/accept-field-ty
Browse files Browse the repository at this point in the history
Accept field(ty = "...") to ease migration to syn 2.0
  • Loading branch information
TedDriggs authored Feb 2, 2024
2 parents 1b3017d + 02589bf commit 977d023
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 16 deletions.
3 changes: 3 additions & 0 deletions derive_builder/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## Unreleased
- Accept `field(ty = "...")` as an alias for `field(type = "...")` in preparation for moving to syn 2.0, which doesn't allow the use of keywords as meta item paths.

## [0.13.0] - 2024-01-22
- Bump MSRV to 1.56.0
- Add `build_fn(error(validation_error = <bool>))` to disable generation of `ValidationError` within the builder's error so that `alloc::string` is avoided.
Expand Down
8 changes: 4 additions & 4 deletions derive_builder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -645,13 +645,13 @@
//! #[derive(Debug, PartialEq, Default, Builder, Clone)]
//! #[builder(derive(Debug, PartialEq))]
//! struct Lorem {
//! #[builder(setter(into), field(type = "u32"))]
//! #[builder(setter(into), field(ty = "u32"))]
//! ipsum: u32,
//!
//! #[builder(field(type = "String", build = "()"))]
//! #[builder(field(ty = "String", build = "()"))]
//! dolor: (),
//!
//! #[builder(field(type = "&'static str", build = "self.amet.parse()?"))]
//! #[builder(field(ty = "&'static str", build = "self.amet.parse()?"))]
//! amet: u32,
//! }
//!
Expand All @@ -670,7 +670,7 @@
//! # }
//! ```
//!
//! The builder field type (`type =`) must implement `Default`.
//! The builder field type (`ty =`) must implement `Default`.
//!
//! The argument to `build` must be a literal string containing Rust code for the contents of a block, which must evaluate to the type of the target field.
//! It may refer to the builder struct as `self`, use `?`, etc.
Expand Down
4 changes: 2 additions & 2 deletions derive_builder/tests/builder_field_custom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ use std::num::ParseIntError;

#[derive(Debug, PartialEq, Default, Builder, Clone)]
pub struct Lorem {
#[builder(field(type = "Option<usize>", build = "self.ipsum.unwrap_or(42) + 1"))]
#[builder(field(ty = "Option<usize>", build = "self.ipsum.unwrap_or(42) + 1"))]
ipsum: usize,

#[builder(setter(into), field(type = "String", build = "self.dolor.parse()?"))]
#[builder(setter(into), field(ty = "String", build = "self.dolor.parse()?"))]
dolor: u32,
}

Expand Down
11 changes: 8 additions & 3 deletions derive_builder/tests/compile-fail/builder_field_custom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@ pub struct Lorem {
)]
ipsum: usize,

// `default` is incompatible with `field.type`, even without `field.build`
#[builder(default = "2", field(type = "usize"))]
// Both `ty` and `type` are temporarily allowed to ease the transition
// to syn 2.0, but they are mutually exclusive.
#[builder(field(ty = "usize", type = "usize"))]
dolor: usize,

// `default` is incompatible with `field.ty`, even without `field.build`
#[builder(default = "2", field(ty = "usize"))]
sit: usize,

// Both errors can occur on the same field
#[builder(default = "3", field(type = "usize", build = "self.ipsum + 42"))]
#[builder(default = "3", field(ty = "usize", build = "self.ipsum + 42"))]
amet: usize,
}

Expand Down
18 changes: 12 additions & 6 deletions derive_builder/tests/compile-fail/builder_field_custom.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,26 @@ error: #[builder(default)] and #[builder(field(build="..."))] cannot be used tog
8 | default = "1",
| ^^^

error: duplicate field - `type` is a deprecated alias for `ty`.
--> tests/compile-fail/builder_field_custom.rs:15:35
|
15 | #[builder(field(ty = "usize", type = "usize"))]
| ^^^^

error: #[builder(default)] and #[builder(field(type="..."))] cannot be used together
--> tests/compile-fail/builder_field_custom.rs:14:25
--> tests/compile-fail/builder_field_custom.rs:19:25
|
14 | #[builder(default = "2", field(type = "usize"))]
19 | #[builder(default = "2", field(ty = "usize"))]
| ^^^

error: #[builder(default)] and #[builder(field(build="..."))] cannot be used together
--> tests/compile-fail/builder_field_custom.rs:18:25
--> tests/compile-fail/builder_field_custom.rs:23:25
|
18 | #[builder(default = "3", field(type = "usize", build = "self.ipsum + 42"))]
23 | #[builder(default = "3", field(ty = "usize", build = "self.ipsum + 42"))]
| ^^^

error: #[builder(default)] and #[builder(field(type="..."))] cannot be used together
--> tests/compile-fail/builder_field_custom.rs:18:25
--> tests/compile-fail/builder_field_custom.rs:23:25
|
18 | #[builder(default = "3", field(type = "usize", build = "self.ipsum + 42"))]
23 | #[builder(default = "3", field(ty = "usize", build = "self.ipsum + 42"))]
| ^^^
30 changes: 29 additions & 1 deletion derive_builder_core/src/macro_options/darling_opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,23 +215,51 @@ impl Visibility for StructLevelFieldMeta {
}
}

fn preserve_field_span(meta: &Meta) -> darling::Result<(Span, syn::Type)> {
match meta {
Meta::Path(_) => Err(Error::unsupported_format("word").with_span(meta)),
Meta::List(_) => Err(Error::unsupported_format("list").with_span(meta)),
Meta::NameValue(mnv) => Ok((mnv.path.span(), syn::Type::from_value(&mnv.lit)?)),
}
}

/// Contents of the `field` meta in `builder` attributes at the field level.
//
// This is a superset of the attributes permitted in `field` at the struct level.
// Perhaps in the future we will be able to use `#[darling(flatten)]`, but
// that does not exist right now: https://github.com/TedDriggs/darling/issues/146
#[derive(Debug, Clone, Default, FromMeta)]
#[darling(and_then = "Self::finalize")]
pub struct FieldLevelFieldMeta {
public: Flag,
private: Flag,
vis: Option<syn::Visibility>,
#[darling(rename = "type", with = "preserve_field_span", map = "Some", default)]
builder_type_old: Option<(Span, syn::Type)>,
/// Custom builder field type
#[darling(rename = "type")]
#[darling(rename = "ty")]
builder_type: Option<syn::Type>,
/// Custom builder field method, for making target struct field value
build: Option<BlockContents>,
}

impl FieldLevelFieldMeta {
fn finalize(mut self) -> darling::Result<Self> {
if let Some((type_field_span, ty)) = self.builder_type_old.take() {
if self.builder_type.is_some() {
return Err(Error::custom(
"duplicate field - `type` is a deprecated alias for `ty`.",
)
.with_span(&type_field_span));
}

self.builder_type = Some(ty);
}

Ok(self)
}
}

impl Visibility for FieldLevelFieldMeta {
fn public(&self) -> &Flag {
&self.public
Expand Down

0 comments on commit 977d023

Please sign in to comment.