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

Getting Started

Installation

Add zyn to your proc-macro crate:

[dependencies]
zyn = "0.3"

Zyn re-exports syn, quote, and proc-macro2 — you don’t need to add them separately. Access them as zyn::syn, zyn::quote, and zyn::proc_macro2.

Feature flags

FeatureDefaultDescription
deriveyesProc macros (zyn!, debug!, #[zyn::element], #[zyn::pipe], #[zyn::derive], #[zyn::attribute]), extractors (Extract<T>, Attr<T>, Fields, Variants, Data<T>), diagnostics (error!, warn!, note!, help!, bail!), and #[derive(Attribute)]
extnoAttrExt and AttrsExt traits for parsing syn::Attribute values

To enable ext:

[dependencies]
zyn = { version = "0.3", features = ["ext"] }

Your first template

The zyn! macro is a template engine that returns a zyn::TokenStream. Everything outside {{ }} and @ directives passes through as literal tokens, just like quote!:

use zyn::prelude::*;

let name = &input.ident;
let tokens: zyn::TokenStream = zyn! {
    pub struct {{ name }}Builder {
        ready: bool,
    }
};

{{ expr }} interpolates any value that implements ToTokens — identifiers, types, expressions, even other token streams.

Pipes

Pipes transform interpolated values inline with |:

zyn! {
    pub fn {{ name | snake }}(&self) -> &Self {
        &self
    }
}

Chain multiple pipes and format identifiers:

{{ name | snake | ident:"get_{}" }}

Built-in pipes include snake, camel, pascal, screaming, kebab, upper, lower, plural, singular, trim, str, ident, and fmt. See Pipes for the full list.

Control flow

Templates support @if, @for, and @match directives:

zyn! {
    impl {{ ident }} {
        @for (field in fields.iter()) {
            pub fn {{ field.ident | snake }}(&self) -> &{{ field.ty }} {
                &self.{{ field.ident }}
            }
        }
    }
}

Conditionals:

zyn! {
    @if (is_pub) { pub } struct {{ name }} {
        @for (field in fields.iter()) {
            @if (field.vis == syn::Visibility::Public(Default::default())) {
                {{ field.ident }}: {{ field.ty }},
            }
        }
    }
}

See Control Flow for @match and nested directives.

Elements

When a template pattern repeats, extract it into a reusable element with #[zyn::element]:

#[zyn::element]
fn getter(name: zyn::syn::Ident, ty: zyn::syn::Type) -> zyn::TokenStream {
    zyn::zyn! {
        pub fn {{ name | snake | ident:"get_{}" }}(&self) -> &{{ ty }} {
            &self.{{ name }}
        }
    }
}

Invoke elements inside templates with @:

zyn! {
    impl {{ ident }} {
        @for (field in fields.iter()) {
            @getter(
                name = field.ident.clone().unwrap(),
                ty = field.ty.clone(),
            )
        }
    }
}

Elements are compiled like function calls — they accept typed parameters and return a TokenStream. See Elements for children, extractors, and diagnostics.

Wiring it up

Use #[zyn::derive] to turn your templates into a real proc macro. Parameters marked #[zyn(input)] are automatically extracted from the derive input:

#[zyn::element]
fn getter(name: zyn::syn::Ident, ty: zyn::syn::Type) -> zyn::TokenStream {
    zyn::zyn! {
        pub fn {{ name | snake | ident:"get_{}" }}(&self) -> &{{ ty }} {
            &self.{{ name }}
        }
    }
}

#[zyn::derive]
fn my_getters(
    #[zyn(input)] ident: zyn::Extract<zyn::syn::Ident>,
    #[zyn(input)] fields: zyn::Fields,
) -> zyn::TokenStream {
    zyn::zyn! {
        impl {{ ident }} {
            @for (field in fields.iter()) {
                @getter(
                    name = field.ident.clone().unwrap(),
                    ty = field.ty.clone(),
                )
            }
        }
    }
}

Users apply it like any derive macro — the function name my_getters becomes MyGetters:

#[derive(MyGetters)]
struct User {
    name: String,
    age: u32,
}

// Generated:
// impl User {
//     pub fn get_name(&self) -> &String { &self.name }
//     pub fn get_age(&self) -> &u32 { &self.age }
// }

See Proc Macro Entry Points for #[zyn::attribute], custom names, helper attributes, and more.

Next steps