gpui-form-derive 0.7.0

Macro crate for gpui-form
Documentation
use super::support::*;

#[test]
fn generated_form_field_enum_contains_only_component_fields() {
    let derive_input: DeriveInput = syn::parse_quote! {
        #[derive(GpuiForm)]
        #[gpui_form(no_inventory)]
        struct UserProfile {
            #[gpui_form(component(crate::Input))]
            display_name: String,
            #[gpui_form(hidden)]
            internal_note: String,
            #[gpui_form(skip)]
            database_id: u64,
        }
    };

    let expanded = expansion::expand_gpui_form(
        derive_input,
        structs::GpuiFormOptions {
            generate_shape: false,
            generate_mcp: false,
        },
    );
    let file = syn::parse2::<syn::File>(expanded.clone()).expect("expansion should parse");
    let form_field = file
        .items
        .iter()
        .find_map(|item| match item {
            syn::Item::Enum(item) if item.ident == "UserProfileFormField" => Some(item),
            _ => None,
        })
        .expect("generated form-field enum");

    let variants = form_field
        .variants
        .iter()
        .map(|variant| variant.ident.to_string())
        .collect::<Vec<_>>();
    assert_eq!(variants, ["DisplayName"]);

    let compact = compact_tokens(&expanded.to_string());
    assert!(
        compact.contains("#[strum(to_string=\"display_name\")]DisplayName"),
        "form-field enum should retain the exact source field name: {compact}"
    );
    assert!(
        compact.contains("impl::gpui_form::core::FormFieldforUserProfileFormField"),
        "form-field enum should implement the facade contract: {compact}"
    );
    assert!(
        compact.contains("pubconstfnname(self)->&'staticstr{self.into_str()}"),
        "form-field enum should expose a const static name: {compact}"
    );
}

#[test]
fn test_structured_field_attribute_grammar() {
    let tokens = quote! {
        #[derive(GpuiForm)]
        struct TestForm {
            #[gpui_form(component(
                crate::DatePickerState,
                value(
                    type = chrono::NaiveDate,
                    from_source = |ts| to_form(ts),
                    into_source = |dt| to_model(dt)
                ),
                default = Timestamp::now()
            ))]
            birth_date: Timestamp,

            #[gpui_form(hidden(
                value(type = String, from_source = |id| id.to_string(), into_source = |id| id.parse().unwrap()),
                default = 1_u64
            ))]
            account_id: u64,
        }
    };

    let derive_input: DeriveInput = syn::parse2(tokens).unwrap();
    let expanded = expansion::expand_gpui_form(
        derive_input,
        structs::GpuiFormOptions {
            generate_shape: true,
            generate_mcp: false,
        },
    );

    let compact = compact_tokens(&expanded.to_string());

    assert!(
        compact.contains("RustType::from_macro_tokens_unchecked(\"chrono::NaiveDate\")")
            && compact.contains("pubaccount_id:String"),
        "structured value(type = ...) should drive component metadata and hidden holder storage: {compact}"
    );
    assert!(
        compact.contains("to_form") && compact.contains("to_model"),
        "structured from_source/into_source conversions should be emitted: {compact}"
    );
    assert!(
        compact.contains(".with_default(::gpui_form::schema::registry::RustExpr::from_macro_tokens_unchecked(\"Timestamp::now()\"))")
            && compact.contains("::core::convert::Into::into(1_u64)"),
        "structured default options should be emitted for inventory and value-holder defaults: {compact}"
    );
    assert!(
        compact.contains("FieldVariant::hidden_for_field(\"account_id\",::gpui_form::schema::registry::FieldValueSpec::new(::gpui_form::schema::registry::RustType::from_macro_tokens_unchecked(\"String\"),::gpui_form::schema::registry::RustType::from_macro_tokens_unchecked(\"u64\"),::gpui_form::schema::registry::FieldValuePresence::DirectStorage)")
            && compact.contains("with_conversions(::gpui_form::schema::registry::ConversionMetadata::new(Some(::gpui_form::schema::registry::RustExpr::from_macro_tokens_unchecked(\"|id|id.to_string()\")),Some(::gpui_form::schema::registry::RustExpr::from_macro_tokens_unchecked(\"|id|id.parse().unwrap()\"))))")
            && compact.contains(".with_default(::gpui_form::schema::registry::RustExpr::from_macro_tokens_unchecked(\"1_u64\"))"),
        "hidden fields should be emitted into inventory with source/form types, conversions, direct storage, and defaults: {compact}"
    );
}

#[test]
fn test_gpui_form_rejects_duplicate_field_options() {
    let tokens = quote! {
        #[derive(GpuiForm)]
        struct TestForm {
            #[gpui_form(component(crate::Input, value(type = String, type = std::string::String)))]
            name: String,

            #[gpui_form(skip, skip)]
            hidden: bool,
        }
    };

    let derive_input: DeriveInput = syn::parse2(tokens).unwrap();
    let expanded = expansion::expand_gpui_form(
        derive_input,
        structs::GpuiFormOptions {
            generate_shape: true,
            generate_mcp: false,
        },
    );

    let compact = compact_tokens(&expanded.to_string());

    assert!(
        compact.contains("duplicate`type`option")
            && compact.contains("removetheduplicate`type`entry"),
        "duplicate type should produce an actionable error: {compact}"
    );
}