zyn 0.5.4

A proc macro framework with templates, composable elements, and built-in diagnostics
Documentation
use zyn::ext::{FieldKey, FieldsExt};
use zyn::syn;

#[test]
fn is_named_for_braced_struct() {
    let input: syn::DeriveInput =
        zyn::parse!("struct Foo { x: i32, y: String }" => syn::DeriveInput).unwrap();
    let data = match &input.data {
        syn::Data::Struct(s) => &s.fields,
        _ => panic!("expected struct"),
    };
    assert!(data.is_named());
    assert!(!data.is_unnamed());
    assert!(!data.is_unit());
}

#[test]
fn is_unnamed_for_tuple_struct() {
    let input: syn::DeriveInput =
        zyn::parse!("struct Foo(i32, String);" => syn::DeriveInput).unwrap();
    let data = match &input.data {
        syn::Data::Struct(s) => &s.fields,
        _ => panic!("expected struct"),
    };
    assert!(data.is_unnamed());
    assert!(!data.is_named());
}

#[test]
fn get_field_by_name() {
    let input: syn::DeriveInput =
        zyn::parse!("struct Foo { name: String, age: u32 }" => syn::DeriveInput).unwrap();
    let data = match &input.data {
        syn::Data::Struct(s) => &s.fields,
        _ => panic!("expected struct"),
    };
    let key = FieldKey::from("name");
    assert!(data.get(&key).is_some());
}

#[test]
fn get_field_by_index() {
    let input: syn::DeriveInput =
        zyn::parse!("struct Foo(i32, String);" => syn::DeriveInput).unwrap();
    let data = match &input.data {
        syn::Data::Struct(s) => &s.fields,
        _ => panic!("expected struct"),
    };
    assert!(data.get(&FieldKey::from(0usize)).is_some());
    assert!(data.get(&FieldKey::from(1usize)).is_some());
    assert!(data.get(&FieldKey::from(2usize)).is_none());
}

#[test]
fn keyed_iterates_named_fields() {
    let input: syn::DeriveInput =
        zyn::parse!("struct Foo { x: i32, y: String }" => syn::DeriveInput).unwrap();
    let data = match &input.data {
        syn::Data::Struct(s) => &s.fields,
        _ => panic!("expected struct"),
    };
    let keys: Vec<String> = data.keyed().map(|(k, _)| k.to_string()).collect();
    assert_eq!(keys, vec!["x", "y"]);
}

#[zyn::element]
pub fn option_getters(fields: syn::Fields) -> zyn::TokenStream {
    use zyn::ext::{FieldsExt, TypeExt};

    let mut tokens = zyn::TokenStream::new();
    for (key, field) in fields.keyed() {
        let ty = &field.ty;
        if field.is_option() {
            let inner = field.inner_type().unwrap();
            let part: zyn::TokenStream = zyn::zyn!(
                fn {{ key | ident:"get_{}" }}(&self) -> Option<&{{ inner }}> {
                    self.{{ key }}.as_ref()
                }
            )
            .into();
            tokens.extend(part);
        } else {
            let part: zyn::TokenStream = zyn::zyn!(
                fn {{ key | ident:"get_{}" }}(&self) -> &{{ ty }} {
                    &self.{{ key }}
                }
            )
            .into();
            tokens.extend(part);
        }
    }
    tokens
}

#[test]
fn element_renders_option_aware_getters() {
    let input: zyn::Input =
        zyn::parse!("struct User { name: String, email: Option<String> }" => syn::DeriveInput)
            .unwrap()
            .into();
    let fields = match input.as_derive().unwrap().data.clone() {
        syn::Data::Struct(s) => s.fields,
        _ => panic!("expected struct"),
    };

    let output = zyn::zyn!(@option_getters(fields = fields));
    zyn::assert_tokens_contain!(output, "get_name");
    zyn::assert_tokens_contain!(output, "get_email");
    zyn::assert_tokens_contain!(output, "as_ref");
}