Crate subdef

Crate subdef 

Source
Expand description

crates.io docs.rs license msrv github

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.

§Usage

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,
}

§How it works

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 address field contains _, which infers to be Address.
  • The friends field contains _, which infers to be Friend, so Vec<_> is inferred to Vec<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!

§Propagate attributes

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
}

§Fine-tune propagation

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,
}

Attribute Macros§

subdef