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

Accept field(ty = "...") to ease migration to syn 2.0 #306

Merged
merged 1 commit into from
Feb 2, 2024
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
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
Loading