Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Attributes & Visibility

This document covers how to control derive macros, attribute macros, visibility, and cross-crate export for types generated by derive_struct! and derive_enum!.


Extra Derive Macros

Place #[derive(...)] before the type name to add derive macros to the generated root type:

#![allow(unused)]
fn main() {
use serde::{Serialize, Deserialize};
use yuuka::derive_struct;

derive_struct!(
    #[derive(Serialize, Deserialize)]
    Root {
        name: String,
        value: i32,
    }
);
}

Note: Debug and Clone are always derived automatically. You don’t need to specify them.

The same works for derive_enum!:

#![allow(unused)]
fn main() {
use yuuka::derive_enum;

derive_enum!(
    #[derive(Serialize, Deserialize)]
    enum Status {
        Active,
        Inactive,
    }
);
}

Attribute Macros

Place attribute macros after #[derive(...)]:

#![allow(unused)]
fn main() {
derive_struct!(
    #[derive(Serialize, Deserialize)]
    #[serde(rename_all = "camelCase")]
    Root {
        user_name: String,
        home_dir: String,
    }
);

let json = serde_json::to_string(&Root {
    user_name: "langyo".to_string(),
    home_dir: "/home/langyo".to_string(),
}).unwrap();
assert_eq!(json, r#"{"userName":"langyo","homeDir":"/home/langyo"}"#);
}

Recursive Attribute Propagation

Use #[macros_recursive(...)] to propagate attributes to all nested inline types:

#![allow(unused)]
fn main() {
derive_struct!(
    #[derive(Serialize, Deserialize)]
    #[macros_recursive(serde(rename_all = "camelCase"))]
    Root {
        nick_name: {
            chinese: {
                simplified_chinese: {
                    first_name: {
                        origin: String = "早濑".to_string(),
                        meme: String = "旱濑".to_string(),
                    },
                    last_name: String = "优香".to_string(),
                },
                traditional_chinese: {
                    first_name: String = "早瀨".to_string(),
                    last_name: String = "優香".to_string(),
                },
            },
            japanese: {
                first_name: String = "早瀬".to_string(),
                last_name: String = "ユウカ".to_string(),
            },
        },
    }
);

let json = serde_json::to_string(&Root::default()).unwrap();
// All nested levels use camelCase: "nickName", "simplifiedChinese", "firstName", etc.
}

#[macros_recursive(...)] applies the specified attributes to every struct and enum generated in the hierarchy — not just the root type.


Field-level Attributes

Place attributes directly before a field name:

#![allow(unused)]
fn main() {
derive_struct!(
    #[derive(Serialize, Deserialize)]
    #[serde(rename_all = "camelCase")]
    Root {
        nick_name: String,
        #[serde(rename = "location")]
        live_in: String,
    }
);

// "live_in" serializes as "location" instead of "liveIn"
}

Variant-level Attributes for Enums

#![allow(unused)]
fn main() {
derive_enum!(
    #[derive(Serialize, Deserialize)]
    #[serde(rename_all = "snake_case")]
    enum Member {
        SaibaMomoi,
        SaibaMidori,
        #[serde(rename = "yuzu")]
        HanaokaYuzu,
        TendouAris,
    } = HanaokaYuzu
);

let json = serde_json::to_string(&Member::default()).unwrap();
assert_eq!(json, r#""yuzu""#);
}

Type-level Attributes on Inline Types

You can apply #[derive(...)] and attributes to inline struct/enum types defined in a field. Place them before the field name, using #[derive(...)] to separate field attributes from type attributes:

Named Inline Types

#![allow(unused)]
fn main() {
derive_struct!(
    #[derive(Serialize, Deserialize)]
    #[serde(deny_unknown_fields)]
    Root {
        nick_name: String,
        #[serde(rename = "position")]
        #[derive(PartialEq)]
        #[serde(rename_all = "UPPERCASE")]
        location: Location {
            country: String,
            address: String,
        },
    }
);

// Root gets #[serde(deny_unknown_fields)]
// Location gets #[derive(PartialEq)] and #[serde(rename_all = "UPPERCASE")]
// The field "location" is renamed to "position"
}

Anonymous Inline Types

For anonymous types, use #[derive] (empty derive) as a separator:

#![allow(unused)]
fn main() {
derive_struct!(
    #[derive(Serialize, Deserialize)]
    #[serde(deny_unknown_fields)]
    Root {
        nick_name: String,
        #[serde(rename = "position")]
        #[derive]
        #[serde(rename_all = "UPPERCASE")]
        location: {
            country: String = "kivotos".to_string(),
            address: String = "777".to_string(),
        },
    }
);

// The empty #[derive] separates field-level attributes (above) from type-level attributes (below)
}

On Enum Variants

The same pattern works for enum tuple variants:

#![allow(unused)]
fn main() {
derive_enum!(
    #[derive(Serialize, Deserialize)]
    #[serde(deny_unknown_fields)]
    enum Group {
        #[serde(rename = "777")]
        #[derive(PartialEq)]
        #[serde(rename_all = "UPPERCASE")]
        Millennium(enum Millennium {
            GameDevelopment(enum GameDevelopment {
                Momoi,
                Midori,
                Yuzu,
                Arisu,
            }),
            #[serde(rename = "C&C")]
            CAndC,
            Veritas,
        }),
    }
);
}

And for anonymous enum variants:

#![allow(unused)]
fn main() {
derive_enum!(
    #[derive(Serialize, Deserialize)]
    enum Group {
        #[serde(rename = "777")]
        #[derive]
        #[serde(rename_all = "UPPERCASE")]
        Millennium(enum {
            GameDevelopment(enum GameDevelopment {
                Momoi, Midori, Yuzu, Arisu,
            } = Yuzu),
            CAndC,
        } = GameDevelopment(Default::default())),
    } = Millennium(Default::default())
);
}

Visibility

pub Modifier

Use pub to make generated types and their module public:

#![allow(unused)]
fn main() {
derive_struct!(
    pub Root {
        name: String,
    }
);

derive_enum!(
    pub enum Status {
        Active,
        Inactive,
    }
);
}

This generates pub mod __Root and pub use __Root::*, making all types accessible from outside the current module.

Default Visibility

Without pub, types are pub(crate):

#![allow(unused)]
fn main() {
derive_struct!(
    Root {
        name: String,
    }
);
// Generates: pub(crate) mod __Root { ... }
// Generates: pub(crate) use __Root::*;
}

Note: pub declarations are typically used at the module or crate level (outside of functions). Inside test functions, visibility doesn’t matter.


Cross-Crate Usage

To export generated types and their auto! helper macros for use in other crates, use #[macro_export]:

Library Crate

#![allow(unused)]
fn main() {
use yuuka::{derive_struct, derive_enum};

derive_struct!(
    #[derive(PartialEq)]
    #[macro_export]
    pub TestStruct {
        a: i32,
        b: String,
        c: {
            d: i32,
            e: String,
        },
    }
);

derive_enum!(
    #[macro_export]
    #[derive(PartialEq)]
    pub enum TestEnum {
        A(i32),
        B(String),
        C(enum C {
            D(i32),
            E(String),
            F(enum F {
                G(i32),
                H(String),
            }),
        }),
    }
);
}

Note: #[macro_export] can be placed before or after #[derive(...)] — both positions work.

Consuming Crate

#![allow(unused)]
fn main() {
use yuuka::auto;
use my_lib::*;

let test_struct = auto!(TestStruct {
    a: 1,
    b: "Hello".to_string(),
    c: {
        d: 2,
        e: "World".to_string(),
    },
});

let test_enum = auto!(TestEnum::C::F::H("Hello".to_string()));
assert_eq!(test_enum, TestEnum::C(C::F(F::H("Hello".to_string()))));
}

How It Works

#[macro_export] makes the generated macro_rules! helper macros (like __auto_TestStruct!) available at the crate root level. Without this attribute, the helper macros are only visible within the defining crate, and auto! won’t work from external crates.

Cargo.toml Setup

For the library crate, ensure it can be linked properly:

[lib]
crate-type = ["rlib", "dylib"]