# `subdef`

<!-- cargo-reedme: start -->

<!-- cargo-reedme: info-start

    Do not edit this region by hand
    ===============================

    This region was generated from Rust documentation comments by `cargo-reedme` using this command:

        cargo +nightly reedme

    for more info: https://github.com/nik-rev/cargo-reedme

cargo-reedme: info-end -->

[![crates.io](https://img.shields.io/crates/v/subdef?style=flat-square&logo=rust)](https://crates.io/crates/subdef)
[![docs.rs](https://img.shields.io/docsrs/subdef?style=flat-square&logo=docs.rs)](https://docs.rs/subdef)
![license](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue?style=flat-square)
![msrv](https://img.shields.io/badge/msrv-1.68-blue?style=flat-square&logo=rust)
[![github](https://img.shields.io/github/stars/nik-rev/subdef)](https://github.com/nik-rev/subdef)

This crate provides an attribute macro [`#[subdef]`](https://docs.rs/subdef/latest/subdef/attr.subdef.html) - it lets you define nested structs and enums “inline”, to keep all definitions in a single place and reduce how much scrolling you have to do

Crates like `subdef` are commonly used to model nested APIs 1:1 using `serde`

```toml
subdef = "0.1"
```

It successor to crates like [`nestify`](https://crates.io/crates/nestify), [`nest_struct`](https://crates.io/crates/nest_struct) and [`structstruck`](https://lib.rs/crates/structstruck).

What distinguishes `subdef` from the others is that types that have `#[subdef]` applied to them can be fully formatted by `rustfmt`.

## Usage

Apply `#[subdef]` to your type to be able to define inline types in individual fields

```rust
#[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:

```rust
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:

```rust
#[subdef]
pub enum One {
    Two([_; { pub struct Two; }])
}
```

Inline types can contain fields that have inline types themselves:

```rust
#[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

```rust
#[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:

```rust
#[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

**Note:** This is an advanced section, most use cases won’t need this feature

You can attach labels to each attribute:

```rust
#[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:

```rust
#[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:

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

## Global Import

This will make `#[subdef]` globally accessible in your entire crate, without needing to import it:

```rust
#[macro_use(subef)]
extern crate subdef;
```

<!-- cargo-reedme: end -->
