Crates.io | extruct |
lib.rs | extruct |
version | 0.2.1 |
source | src |
created_at | 2024-08-23 20:51:30.25539 |
updated_at | 2024-10-22 14:04:05.436026 |
description | Extruct is a proc-macro library that provides tools for listing named struct fields and implementing conversion from a larger struct containing fields with same names. |
homepage | |
repository | https://codeberg.org/vkrivopalov/extruct |
max_upload_size | |
id | 1349614 |
size | 28,821 |
Extruct is a proc-macro library that provides tools for listing named struct fields and implementing conversion from a larger struct containing fields with same names.
Extruct is primarily driven by the following use-case.
It is customary for REST API to return a whole bunch of properties of an object
in a JSON response. Oftentimes, you as a user of this API are only interested in
a few of those and don't really need others.
To facilitate this, REST services typically support passing the list of properties
the client is interested in through the fields
query parameter.
See examples of this pattern here
and here.
Extruct offers two simple-to-use procedural macros that can be useful in implementing
wrappers around REST API that can retrieve only specific properties from the backend.
The first macro is a derive macro Fields
. It applies to structs with named
fields and implements the Fields
trait that returns a list of fields' names as string
literals.
The second macro is an attribute macro extruct_from
.
It can be used when there exists a "superstruct" that contains all possible fields.
In this case, when defining your own struct which contains only a subset of all
those fields you can use extruct_from
which will implement the
std::convert::From
trait
converting an instance of the superstruct into an instance of your struct by converting
all fields from the original struct into fields with the same name of the substruct.
Besides being handy for turning "full" objects into "partial", this ensures that all fields are named the same and their types are compatible. To prove this, [extruct_from
] implements the [ExtructedFrom
] trait which extends std::convert::From
and verifies that the superstruct is not only convertible into substruct but also that all the respective fields have same names.
use extruct::ExtructedFrom;
use extruct::Fields;
use extruct::extruct_from;
#[derive(Fields)]
struct SuperStruct {
one_field: String,
another_field: u32,
and_one_more: char,
}
#[derive(Fields)]
#[extruct_from(SuperStruct)]
struct SubStruct {
and_one_more: String,
}
fn convert_preserving_names<T, S>(sup: S) -> T
where
T: ExtructedFrom<S>,
{
sup.into()
}
assert_eq!(SuperStruct::fields(), ["one_field", "another_field", "and_one_more"]);
assert_eq!(SubStruct::fields(), ["and_one_more"]);
let sup = SuperStruct {
one_field: "str".to_owned(),
another_field: 1135,
and_one_more: 'x',
};
let sub: SubStruct = sup.into();
assert_eq!(sub.and_one_more, "x".to_owned());
The Fields
derive macro can be applied to named structs and unit structs,
but unnamed structs can only be annotated with #[derive(Fields)]
if they are empty.
The extruct_from
attribute macro can only be applied to non-generic named structs
and can only reference a concrete (non-generic) struct as a superstruct.
This limitation holds because the macro does not have visibility into the definition
of the superstruct and therefore would not be able to specify trait bounds for generic
fields when implementing the std::convert::From
trait.
If you want to use a specific instantiation of a generic struct as a superstruct, you
can use a type alias like so:
// A generic struct cannot be referenced by the extruct_from() attribute macro...
struct GenericStruct<T, S> {
one_field: T,
another_field: u32,
and_one_more: S,
}
// ... but a non-generic type alias can be referenced by the extruct_from()
// attribute macro just fine
type ConcreteStruct = GenericStruct<String, char>;
#[extruct_from(ConcreteStruct)]
struct SubStruct {
and_one_more: String,
}
let sup = ConcreteStruct {
one_field: "str".to_owned(),
another_field: 1135,
and_one_more: 'x',
};
let sub: SubStruct = sup.into();
assert_eq!(sub.and_one_more, "x".to_owned());