Feature/attribute passthrough - #248
Merged
Merged
Conversation
Implements attribute passthrough (#229): * Struct-level attributes (e.g. #[repr(transparent)], #[sqlx(transparent)]) and field-level attributes (e.g. #[garde(length(min = 1))]) are forwarded verbatim onto the generated type, so third-party derives pulled in via derive_unchecked can read them. Previously struct attributes were rejected and field attributes were silently dropped. Resolves #228 and the attribute half of #191. Forwarded attributes are emitted after the generated #[derive(...)], because derive-helper attributes are only legal after the derive that introduces them (legacy_derive_helpers). * Serde customization (#201): nutype's generated Serialize/Deserialize impls honor field-level #[serde(with = "...")], serialize_with, deserialize_with and struct-level #[serde(transparent)], using serde's own syntax. The serialize_with path mirrors serde's derive (a wrapper struct inside newtype framing); transparent drops the framing entirely. Sanitization and validation ALWAYS still run on deserialization: a custom deserialize_with function only produces the raw inner value, which is then routed through try_new/new. * Other serde keys, #[schemars(...)], #[cfg] on the inner field and native #[derive(...)] get targeted errors. Item-level #[cfg] needs no handling: rustc strips it before the macro expands (pinned by a functional test). * Fix: field attributes on "any" inner types no longer leak into type positions of the generated code (AnyInnerType carried the whole syn::Field including attributes, which broke e.g. fn try_new(raw_value: #[attr] T)). Tests: functional suite in test_suite/tests/attr_passthrough.rs (forwarding, serde matrix incl. RON framing, guarantee tests proving validation gates custom deserialization), UI fixtures for every targeted error plus an observable forwarding proof via deny(deprecated), and third-party derive proofs (arbitrary field attr, derive_more::Display type attr) in examples/derive_unchecked_example. The test_suite deliberately does not gain a derive_unchecked feature: several UI fixtures pin the feature-off error messages and would flip under --all-features.
README and macro docs: new "Attribute passthrough" section with sqlx/garde recipes, the serde customization subsection, and the boundary note (nutype forwards attributes verbatim and cannot verify what they do; an attribute macro that rewrites the type can break the guarantees, exactly like derive_unchecked).
|
@greyblake, thank you! |
Owner
Author
|
Available now in 0.8.0-beta.1. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Attribute passthrough and serde customization
Attributes written on the struct and on the inner field are now forwarded verbatim onto the generated type, so the compiler and third-party derives (via
derive_unchecked) can read them.Previously struct attributes were rejected and field attributes were silently dropped.
This makes nutype compose with crates like
sqlx,gardeandzeroizewithout nutype having to know about them. (In the longer run the plan is to support such popular crates natively, with invariant-aware derives that do not requirederive_unchecked; passthrough is the general escape hatch that workstoday.)
Serde attributes are the exception: nutype hand-writes its
Serialize/Deserializeimpls, so instead of forwarding, it now understands field-level#[serde(with = "...")],serialize_with,deserialize_withand type-level#[serde(transparent)]natively. Sanitization and validation always still run on deserialization, even with a customdeserialize_withfunction, so the core guarantee is preserved:Closes #229, partially address #228, closes #201. Also unblocks the
sqlx(transparent)and zeroize asks in #191 and makes #214 (borsh) easier. Covered by a new functional suite (including guarantee tests proving validation gates custom deserialization), UI fixtures for every targeted error, and third-party derive proofs inexamples/derive_unchecked_example. Targeted at 0.8.0, since previously dropped field attributes becoming live is an observable behavior change. Also fixes a latent bug where field attributes on custom ("any") inner types leaked into type positions of the generated code.