shrinkwrap
Shrinkwrap makes it easy to render additional variations for a given set (or subset) of data.
A common use-case is providing human-readable variations of some coded format in an API response (e.g. timestamps, numerics, currency balances, unit conversions).
Overview
Consider the following struct which is used in an API response.
Suppose you wanted to provide API clients with balance and timestamps ready for display, e.g. $123.45 USD.
Shrinkwrap allows for single-call conversion from
To
Shrinkwrap will provide conversion when you add #[derive(Wrap)] and a few required attributes to your
struct, plus a function to handle the actual conversion.
Alternative solutions
Alternatively, you could...
-
Newtype wrap your fields, providing a
Displayimpl to override the original display format. -
Or, use
#[serde(with = ...)]to provide custom serialization for the field.
If these suit your needs, great!
But sometimes you need more control.
What shrinkwrap can do:
- Provide variants of data alongside the original, "untransformed" data
- Conditionally include/exclude variant groups at run-time
- Allow for handling conversions that are dependent on user-data (e.g. locale, user settings)
- Provide trivial support for chaining variations off of one another (using the output of one as input for another).
- Process conversions which have service dependencies
[!NOTE] Alternatively, you could add fields for each variation directly into your main struct. e.g.
idusernamebalancebalance_textbalance_localbalance_local_textlast_loginlast_login_textHowever, this clearly becomes unweildy:
Your structs become bloated with this extra baggage and conversions are being done all over the place.
Usage
Minimal example
To accomplish the example from the Overview section:
-
Define your
transformstruct. This is the type that will be used to handle conversions.use Transform; -
Annotate your data struct, specifying the transform type from step 1, the nest definition, and fields to include in the nest
use Wrap; // associate the wrapper/extra/nest conversion to your transform // define a variant group (nest) for text repr -
Add the conversion impl
use TransformToNest; -
Use your derived impls + structs
use ToWrappedWith; // -- snip -- // let transform = MyTransform ; let transform_opts = MyTransformOpts ; let data = UserResponse ; // generate the wrapper with your mapped data nested under 'extra'. let wrapped = data.to_wrapped_with; println!; println!;
The above example will output the following:
Generated wrapper struct debug output:
UserResponseWrapper {
data: UserResponse {
id: 2330f8a8-2f6b-4ed4-81a2-a4500db6ac33,
username: "johndoe",
balance: 27468,
last_login: 2025-08-06T16:32:24Z,
},
extra: UserResponseExtra {
text: UserResponseNestedText {
balance: "$274.68 USD",
last_login: "2025-08-06 4:32pm",
},
},
}
Generated wrapper json:
[!NOTE] This example can be viewed and compiled in full at
examples/readme
The shrinkwrap hierarchy
Shrinkwrap generates the following:
- A dedicated
Neststruct for each defined variation set - An
Extrastruct that contains all associated nests - A
Wrapperstruct containing the original data struct, and theExtrastruct.- Note: in the JSON above, the original data field of the wrapper has
#[serde(flatten)]applied to it, giving the appearance of inlined data. This is done to reduce excessive nesting for consumers.
- Note: in the JSON above, the original data field of the wrapper has
Tree diagram:
- wrapper
- data (original data struct, which gets inlined into the wrapper)
* field1
* field2
- extra
- nest1:
* field1
* field2
- nest2:
* field1
* field2
Variations are placed in a dedicated struct (the nests) to avoid polluting source data sets. Each data set can support multiple nests, where each provides a distinct variation of a subset of the source data's fields.
Nest chaining
Nests can be branched off one another, allowing for chained variations. An example of such flow:
balancein USD cents- to
balancein local currency - and finally to
balancein human readabable local currency
Continuing with the first JSON example, where we are chaining:
balancein USD centsbalancein USD in a human-readable formatbalancein local currencybalancein local currency in a human-readable format
Attribute Reference
[!CAUTION] Work in progress - will be finished in the next patch
Links
License
This project is licensed under the MIT license.