xylem-codegen 0.2.7

Codegen backend for xylem
Documentation
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;

use crate::tests::token_stream_equals;
use crate::{process_field, FieldConv, FieldFrom};

fn test_process_field(fields: TokenStream, expects: &[(Option<FieldFrom>, FieldConv)]) {
    let full = quote! {
        struct Test #fields
    };
    let data = syn::parse2::<syn::ItemStruct>(full).expect("Invalid test case");

    for (field, (expect_from, expect_conv)) in data.fields.iter().zip(expects.iter()) {
        let (actual_from, actual_conv) = process_field(
            field,
            quote!(_from_placeholder_),
            &syn::parse2::<syn::Type>(quote!(::_placeholder_::_Schema_))
                .expect("Cannot parse literal token stream"),
        )
        .expect("Invalid test case");

        match (expect_from, &actual_from) {
            (None, None) => {}
            (Some(expect), Some(actual)) => {
                assert!(
                    token_stream_equals(expect.attrs.clone(), actual.attrs.clone()),
                    "Expected FieldFrom.attrs =\n{}\n, actual FieldFrom.attrs =\n{}\n",
                    &expect.attrs,
                    &actual.attrs,
                );

                match (&expect.ident, &actual.ident) {
                    (None, None) => {}
                    (Some(expect), Some(actual)) => {
                        assert_eq!(expect.to_string(), actual.to_string());
                    }
                    _ => panic!(
                        "Expected FieldFrom.ident = {:?}, actual FieldFrom.ident = {:?}",
                        expect, actual
                    ),
                }

                assert!(
                    token_stream_equals(expect.ty.clone(), actual.ty.clone()),
                    "Expected FieldFrom.ty =\n{}\n, actual FieldFrom.ty =\n{}\n",
                    &expect.ty,
                    &actual.ty,
                );
            }
            _ => panic!("Expected FieldFrom = {:?}, got {:?}", expect_from, actual_from),
        }

        match (&expect_conv.ident, &actual_conv.ident) {
            (None, None) => {}
            (Some(expect), Some(actual)) => {
                assert_eq!(expect.to_string(), actual.to_string());
            }
            (expect, actual) => panic!(
                "Expected FieldConv.ident = {:?}, actual FieldConv.ident = {:?}",
                expect, actual,
            ),
        }

        assert!(
            token_stream_equals(expect_conv.expr.clone(), actual_conv.expr.clone()),
            "Expected FieldConv.expr =\n{}\n, actual FieldConv.expr =\n{}\n",
            &expect_conv.expr,
            &actual_conv.expr
        );
    }
}

#[test]
fn test_field_standard_named() {
    test_process_field(
        quote!({
            foo: Bar,
        }),
        &[(
            Some(FieldFrom {
                attrs: quote! {},
                ident: Some(Ident::new("foo", Span::call_site())),
                ty:    quote!(<Bar as ::xylem::Xylem<::_placeholder_::_Schema_>>::From),
            }),
            FieldConv {
                ident: Some(Ident::new("foo", Span::call_site())),
                expr:  quote! {{
                    type Args = <Bar as ::xylem::Xylem<::_placeholder_::_Schema_>>::Args;
                    ::xylem::lazy_static! {
                        static ref __XYLEM_ARGS: Args = Args { ..::std::default::Default::default() };
                    }
                    ::xylem::Xylem::<::_placeholder_::_Schema_>::convert(
                        _from_placeholder_,
                        __xylem_context,
                        &*__XYLEM_ARGS,
                    )?
                }},
            },
        )],
    );
}

#[test]
fn test_field_standard_unnamed() {
    test_process_field(
        quote!((Bar);),
        &[(
            Some(FieldFrom {
                attrs: quote! {},
                ident: None,
                ty:    quote!(<Bar as ::xylem::Xylem<::_placeholder_::_Schema_>>::From),
            }),
            FieldConv {
                ident: None,
                expr:  quote! {{
                    type Args = <Bar as ::xylem::Xylem<::_placeholder_::_Schema_>>::Args;
                    ::xylem::lazy_static! {
                        static ref __XYLEM_ARGS: Args = Args { ..::std::default::Default::default() };
                    }
                    ::xylem::Xylem::<::_placeholder_::_Schema_>::convert(
                        _from_placeholder_,
                        __xylem_context,
                        &*__XYLEM_ARGS,
                    )?
                }},
            },
        )],
    );
}

#[test]
fn test_field_serde() {
    test_process_field(
        quote!({
            #[xylem(serde(tagged))]
            foo: Bar,
        }),
        &[(
            Some(FieldFrom {
                attrs: quote! {
                    #[serde(tagged)]
                },
                ident: Some(Ident::new("foo", Span::call_site())),
                ty:    quote!(<Bar as ::xylem::Xylem<::_placeholder_::_Schema_>>::From),
            }),
            FieldConv {
                ident: Some(Ident::new("foo", Span::call_site())),
                expr:  quote! {{
                    type Args = <Bar as ::xylem::Xylem<::_placeholder_::_Schema_>>::Args;
                    ::xylem::lazy_static! {
                        static ref __XYLEM_ARGS: Args = Args { ..::std::default::Default::default() };
                    }
                    ::xylem::Xylem::<::_placeholder_::_Schema_>::convert(
                        _from_placeholder_,
                        __xylem_context,
                        &*__XYLEM_ARGS,
                    )?
                }},
            },
        )],
    );
}

#[test]
fn test_field_preserve() {
    test_process_field(
        quote!({
            #[xylem(preserve)]
            foo: Bar,
        }),
        &[(
            Some(FieldFrom {
                attrs: quote! {},
                ident: Some(Ident::new("foo", Span::call_site())),
                ty:    quote!(Bar),
            }),
            FieldConv {
                ident: Some(Ident::new("foo", Span::call_site())),
                expr:  quote! {
                    Ok(_from_placeholder_)?
                },
            },
        )],
    );
}

#[test]
fn test_field_transform() {
    test_process_field(
        quote!({
            #[xylem(transform = qux(Corge))]
            foo: Bar,
        }),
        &[(
            Some(FieldFrom {
                attrs: quote! {},
                ident: Some(Ident::new("foo", Span::call_site())),
                ty:    quote!(Corge),
            }),
            FieldConv {
                ident: Some(Ident::new("foo", Span::call_site())),
                expr:  quote! {
                    qux(_from_placeholder_)?
                },
            },
        )],
    );
}

#[test]
fn test_field_transform_context() {
    test_process_field(
        quote!({
            #[xylem(transform_with_context = qux(Corge))]
            foo: Bar,
        }),
        &[(
            Some(FieldFrom {
                attrs: quote! {},
                ident: Some(Ident::new("foo", Span::call_site())),
                ty:    quote!(Corge),
            }),
            FieldConv {
                ident: Some(Ident::new("foo", Span::call_site())),
                expr:  quote! {
                    qux(_from_placeholder_, __xylem_context)?
                },
            },
        )],
    );
}

#[test]
fn test_field_default() {
    test_process_field(
        quote!({
            #[xylem(default = qux())]
            foo: Bar,
        }),
        &[(
            None,
            FieldConv {
                ident: Some(Ident::new("foo", Span::call_site())),
                expr:  quote! {
                    qux()
                },
            },
        )],
    );
}

#[test]
fn test_field_args() {
    test_process_field(
        quote!({
            #[xylem(args(foo = bar, qux = corge(1, "waldo")))]
            foo: Bar,
        }),
        &[(
            Some(FieldFrom {
                attrs: quote! {},
                ident: Some(Ident::new("foo", Span::call_site())),
                ty:    quote!(<Bar as ::xylem::Xylem<::_placeholder_::_Schema_>>::From),
            }),
            FieldConv {
                ident: Some(Ident::new("foo", Span::call_site())),
                expr:  quote! {{
                    type Args = <Bar as ::xylem::Xylem<::_placeholder_::_Schema_>>::Args;
                    ::xylem::lazy_static! {
                        static ref __XYLEM_ARGS: Args = Args {
                            foo: bar,
                            qux: corge(1, "waldo"),
                            ..::std::default::Default::default()
                        };
                    }
                    ::xylem::Xylem::<::_placeholder_::_Schema_>::convert(
                        _from_placeholder_,
                        __xylem_context,
                        &*__XYLEM_ARGS,
                    )?
                }},
            },
        )],
    );
}