Expand description
Substruct is a proc-macro to allow you to easily declare structs which are subsets of another struct. It automatically creates conversion methods between the parent and child structs and will propagate any derives and attributes to the child struct.
A basic use of substruct looks like this
use substruct::substruct;
#[substruct(SubQuery)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Query {
#[substruct(SubQuery)]
pub a: &'static str,
pub b: usize
}
let subquery = SubQuery { a: "query" };
let query = Query { a: "query", b: 5 };
assert_eq!(subquery.into_query(5), query);
and that will expand to produce
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Query {
pub a: &'static str,
pub b: usize
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SubQuery {
pub a: &'static str,
}
Substruct isn’t just limited to creating a single child struct, you can use it to create many at once:
#[substruct(Vec2, Vec3)]
pub struct Vec4<T> {
#[substruct(Vec2, Vec3)]
pub x: T,
#[substruct(Vec2, Vec3)]
pub y: T,
#[substruct(Vec3)]
pub z: T,
pub w: T,
}
It is important that the #[substruct]
attribute is placed before other
attributes. The #[substruct]
attribute macro can only see attributes
that come after it, with the exception of doc comments, so any attributes
that are evaluated before it will not be duplicated. While you can use this
for attributes that should only be included in the parent struct, it is
clearer if you use the #[substruct_attr]
attribute macro documented below.
§Overriding documentation for emitted structs and fields
Sometimes you may want to override the emitted documentation for a struct
or field. To do so, document the struct identifier within the #[substruct]
parameters:
/// All the parameters.
#[substruct(
/// A smaller set of parameters.
FilteredParams
)]
#[derive(Clone, Debug)]
pub struct Params {
#[substruct(FilteredParams)]
pub limit: bool,
pub filter: String,
}
For consistency, you can also specify the docs for the parent struct within
the #[substruct]
attribute:
#[substruct(
/// The big kahuna.
BigKahuna,
/// The not-so-big kahuna.
SmallKahuna
)]
#[derive(Clone, Debug)]
pub struct BigKahuna {
pub name: String,
#[substruct(SmallKahuna)]
pub profession: String,
}
§Managing attributes on generated structs
Sometimes you may want attributes to only apply to some of the emitted
structs. To do so, you can use the #[substruct_attr]
macro to only emit
these attributes on the desired structs:
#[substruct(ThingB, ThingC, ThingD)]
#[derive(Serialize, Deserialize)]
pub struct ThingA {
// This field is present in ThingA, ThingB, and ThingC but only has the
// serde rename attribute in ThingB.
#[substruct(ThingB, ThingC)]
#[substruct_attr(ThingB, serde(rename = "a2"))]
pub a: String,
// You can also use the parent struct as a filter.
#[substruct(ThingD, ThingC)]
#[substruct_attr(ThingA, serde(alias = "d"))]
pub b: usize,
}
For more complicated use cases #[substruct_attr]
supports a similar
expression language to the #[cfg]
macro.
#[substruct(A, B, C, D, E, F)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct A {
// This field is available on all structs except and the serde
// attribute is available on all structs except D (and B).
#[substruct(not(B))]
#[substruct_attr(not(D), serde(alias = "f2"))]
pub f1: u32,
}
The expressions you can use here are
<ident>
- true when emitting a struct with the same namenot(<expr>)
- true if the inner expression is falseany(<expr>...)
- true if any of the inner expressions are trueall(<expr>...)
- true if all of the inner expressions are true
On struct fields, the #[substruct]
entries are implicitly wrapped in an
any
expression so you can do:
#[substruct(A, B, C, D, E, F)]
pub struct A {
// This field is available on A, B, C, and D
#[substruct(any(B, C), D)]
pub f1: u32,
}
The parent struct as always implicitly included in the set of structs that each field is emitted for. This means that putting
not(A)
in the the struct above would not exclude the field fromA
(and is, in fact, equivalent toall()
).
On its own, this isn’t too useful, but where it does become useful is when combined with documentation comment overrides.
#[substruct(A, B, C, D, E, F)]
pub struct A {
/// This is the default documentation.
#[substruct(
/// This is the documentation on C, D, and F
any(C, D, F),
/// And this is the documentation on B
B
)]
pub f1: u32,
}
If multiple documentation overrides apply to a single field, then the first one to apply will be used.
§Generics
Generics are currently mostly supported. You can use generics with
#[substruct]
and the macro will expand them just fine:
#[substruct(SmallGeneric)]
pub struct Generic<'a, T> {
pub len: usize,
#[substruct(SmallGeneric)]
pub value: &'a T,
}
However, if one of the child structs doesn’t include a field that uses the generic parameter or lifetime then that will result in an error
#[substruct(NoLifetime)]
pub struct UsesLifetime<'a> {
// ^^ error: lifetime not used in NoLifetime
#[substruct(NoLifetime)]
pub name: String,
pub text: &'a str,
}
Attribute Macros§
- substruct
#[substruct]
attribute macro.