Skip to content

nik-rev/subdef

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

40 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

subdef

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

About

Expressive attribute macro to define nested structures

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Packages

No packages published