# extruct
[![crates.io version](https://img.shields.io/crates/v/extruct)](https://crates.io/crates/extruct)
[![crates.io License](https://img.shields.io/crates/l/extruct)](https://choosealicense.com/licenses/mit/)
## Extruct
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.
### Description
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](https://yandex.com/dev/disk-api/doc/en/reference/all-files)
and [here](https://restcountries.com/#endpoints-filter-response).
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`](https://docs.rs/extruct/latest/extruct/derive.Fields.html). 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`](https://docs.rs/extruct/latest/extruct/attr.extruct_from.html).
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`](https://doc.rust-lang.org/std/convert/trait.From.html) 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`](https://doc.rust-lang.org/std/convert/trait.From.html) and verifies that the superstruct is not only convertible into substruct but also that all the respective fields have same names.
### Examples
```rust
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(sup: S) -> T
where
T: ExtructedFrom,
{
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());
```
## Notes
The [`Fields`](https://docs.rs/extruct/latest/extruct/derive.Fields.html) 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`](https://docs.rs/extruct/latest/extruct/attr.extruct_from.html) 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`](https://doc.rust-lang.org/std/convert/trait.From.html) trait.
If you want to use a specific instantiation of a generic struct as a superstruct, you
can use a type alias like so:
```rust
// A generic struct cannot be referenced by the extruct_from() attribute macro...
struct GenericStruct {
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;
#[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());
```