This crate provides an attribute macro #[subdef] - it simplifies the creation of nested
structures, reduces boilerplate and helps keep logic in a single place.
[dependencies]
subdef = "0.1"It is a successor to nestify.
The biggest distinguishing feature is that items marked with #[subdef] can be
entirely formatted by rustfmt.
Apply #[subdef] to your type to be able to define inline types in individual fields
#[subdef]
struct UserProfile {
name: String,
address: [_; {
struct Address {
street: String,
city: String
}
}],
friends: [Vec<_>; {
struct Friend {
name: String
}
}],
status: [_; {
enum Status {
Online,
Offline,
Idle
}
}]
}Expansion:
struct UserProfile {
name: String,
address: Address,
friends: Vec<Friend>,
status: Status,
}
struct Address {
street: String,
city: String,
}
struct Friend {
name: String,
}
enum Status {
Online,
Offline,
Idle,
}Fields on types marked with #[subdef] can have the type [Type; { Item }] where Type is the actual
type of the field, and Item is the struct or enum.
The Type can contain _, which infers to the name of the Item. In the above example:
- The
addressfield contains_, which infers to beAddress. - The
friendsfield contains_, which infers to beFriend, soVec<_>is inferred toVec<Friend>
You can apply #[subdef] to enums:
#[subdef]
pub enum One {
Two([_; { pub struct Two; }])
}Inline types can contain fields that have inline types themselves:
#[subdef]
struct One {
two: [_; {
struct Two {
three: [_; {
struct Three;
}]
}
}]
}Admittedly, the syntax is a little strange, but that's a small price to pay for the
convenience of automatic formatting by rustfmt!
Give attributes to subdef(...), and they will be propagated recursively to all inline types
#[subdef(derive(Serialize, Deserialize))]
struct SystemReport {
report_id: Uuid,
kind: [_; {
pub enum ReportKind {
Initial,
Heartbeat,
Shutdown,
}
}],
application_config: [_; {
struct ApplicationConfig {
version: String,
container_runtime: String,
flags: [_; {
struct Flags {
is_admin: bool,
is_preview_mode: bool,
telemetry_enabled: bool,
}
}],
components: [Vec<_>; {
struct Component {
name: String,
version: String,
maintainer: Option<String>,
target_platform: String,
}
}],
}
}],
}Expands to this:
#[derive(Serialize, Deserialize)]
struct SystemReport {
report_id: Uuid,
kind: ReportKind,
application_config: ApplicationConfig
}
#[derive(Serialize, Deserialize)]
pub enum ReportKind {
Initial,
Heartbeat,
Shutdown
}
#[derive(Serialize, Deserialize)]
struct ApplicationConfig {
version: String,
container_runtime: String,
flags: Flags,
components: Vec<Component>
}
#[derive(Serialize, Deserialize)]
struct Flags {
is_admin: bool,
is_preview_mode: bool,
telemetry_enabled: bool
}
#[derive(Serialize, Deserialize)]
struct Component {
name: String,
version: String,
maintainer: Option<String>,
target_platform: String
}You can attach labels to each attribute:
#[subdef(
label1 = cfg(not(windows)),
label2 = derive(Serialize, Deserialize)
)]
struct SystemReport { /* ... */ }These are the fine-tuning attributes that you can use:
#[subdef(skip(label1, label2))]to skip applying the attribute to the type#[subdef(skip_recursively(label1, label2))]to recursively skip applying the attribute to the type#[subdef(apply(label1, label2))]to apply the attribute, overriding any previous#[subdef(skip_recursively)]#[subdef(apply_recursively(label1, label2))]to recursively apply the attribute, overriding any previous#[subdef(skip_recursively)]
Example usage of these fine-tuning attributes:
#[subdef(
debug = derive(Debug),
eq = derive(PartialEq, Eq)
)]
#[subdef(skip(debug), skip(eq))]
struct Order {
billing_info: [_; {
#[subdef(skip_recursively(eq))]
struct BillingInfo {
payment_transaction: [_; {
struct TransactionData {
amount_paid_cents: u32,
}
}],
}
}],
shipping_details: [_; {
#[subdef(apply_recursively(eq))]
struct ShippingDetails {
confirmation_status: [_; {
#[subdef(apply(debug))]
struct DetailsConfirmed;
}],
}
}],
}Expansion:
struct Order {
billing_info: BillingInfo,
shipping_details: ShippingDetails,
}
#[derive(Debug)]
struct TransactionData {
amount_paid_cents: u32,
}
#[derive(Debug)]
struct BillingInfo {
payment_transaction: TransactionData,
}
#[derive(PartialEq, Eq, Debug)]
struct DetailsConfirmed;
#[derive(PartialEq, Eq, Debug)]
struct ShippingDetails {
confirmation_status: DetailsConfirmed,
}