# 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()); ```