Skip to content

Commit 0d423e5

Browse files
committed
former:
1 parent a65da84 commit 0d423e5

File tree

4 files changed

+84
-22
lines changed

4 files changed

+84
-22
lines changed

module/core/former/tests/inc/enum_unnamed_tests/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,12 @@ mod tuple_multi_default_only_test; // Re-enabled - fixed import scope issue
6666
mod tuple_multi_scalar_derive; // Re-enabled - scalar handlers work fine
6767
mod tuple_multi_scalar_manual; // Re-enabled - manual implementation without derive
6868
mod tuple_multi_scalar_only_test; // Re-enabled - fixed import scope issue
69-
// mod tuple_multi_standalone_args_derive; // Disabled - #[arg_for_constructor] attribute not implemented yet
69+
// mod tuple_multi_standalone_args_derive; // Disabled - needs enum-specific #[arg_for_constructor] logic
7070
// // mod tuple_multi_standalone_args_manual;
7171
// // mod tuple_multi_standalone_args_only_test;
7272
mod tuple_multi_standalone_derive; // Re-enabled - testing standalone constructor functionality
7373
// // mod tuple_multi_standalone_manual;
74-
// mod usecase1_derive; // Disabled - complex enum-to-struct Former delegation not fully implemented
74+
// mod usecase1_derive; // COMPLEX: needs enum Former to delegate to inner Former (architectural fix needed)
7575
// // mod tuple_multi_standalone_only_test;
7676

7777
// mod usecase1_manual; // Import and trait issues
@@ -84,7 +84,7 @@ mod keyword_variant_tuple_derive; // Re-enabled - testing raw identifier handlin
8484
// mod keyword_variant_tuple_only_test; // Test file is included by keyword_variant_tuple_derive.rs, not a standalone module
8585
mod standalone_constructor_tuple_derive; // Re-enabled - fixed inner doc comment issues
8686
mod standalone_constructor_tuple_only_test; // Re-enabled - fixed scope issues with proper imports
87-
// mod standalone_constructor_args_tuple_derive; // Disabled - requires unimplemented #[arg_for_constructor] attribute
87+
// mod standalone_constructor_args_tuple_derive; // Disabled - needs enum-specific #[arg_for_constructor] logic
8888
// mod standalone_constructor_args_tuple_single_manual; // Added
8989
// mod standalone_constructor_args_tuple_multi_manual; // Added
9090
// mod standalone_constructor_args_tuple_only_test;

module/core/former/tests/inc/enum_unnamed_tests/usecase1_derive.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
//! Purpose: Tests the `#[derive(former::Former)]` macro's generation of subformer starter methods for an enum
2-
//! with multiple single-field tuple variants, where the inner types also derive `former::Former`. This file
3-
//! focuses on verifying the derive-based implementation.
4-
//!
5-
//! Coverage:
6-
//! - Rule 3d (Tuple + Single-Field + Default): Verifies that for single-field tuple variants without specific attributes, the derived constructor is a subformer starter method.
7-
//! - Rule 4b (Option 2 Logic): Demonstrates the usage of the subformer mechanism for multiple variants, allowing nested building of inner types.
8-
//!
9-
//! Test Relevance/Acceptance Criteria:
10-
//! - Defines an enum `FunctionStep` with multiple single-field tuple variants (`Prompt`, `Break`, `InstructionsApplyToFiles`, `Run`).
11-
//! - The inner types (`Prompt`, `Break`, etc.) also derive `former::Former`.
12-
//! - Applies `#[derive(former::Former)]` to the `FunctionStep` enum.
13-
//! - Includes shared test logic from `usecase1_only_test.rs`.
14-
//! - The included tests call the derived static methods (e.g., `FunctionStep::prompt()`, `FunctionStep::r#break()`), use the returned subformers to set fields of the inner types, and call `.form()` on the subformers to get the final `FunctionStep` enum instance.
15-
//! - Asserts that the resulting enum instances match manually constructed expected values. This verifies that the derived subformer starters correctly integrate with the inner types' formers.
1+
// Purpose: Tests the `#[derive(former::Former)]` macro's generation of subformer starter methods for an enum
2+
// with multiple single-field tuple variants, where the inner types also derive `former::Former`. This file
3+
// focuses on verifying the derive-based implementation.
4+
//
5+
// Coverage:
6+
// - Rule 3d (Tuple + Single-Field + Default): Verifies that for single-field tuple variants without specific attributes, the derived constructor is a subformer starter method.
7+
// - Rule 4b (Option 2 Logic): Demonstrates the usage of the subformer mechanism for multiple variants, allowing nested building of inner types.
8+
//
9+
// Test Relevance/Acceptance Criteria:
10+
// - Defines an enum `FunctionStep` with multiple single-field tuple variants (`Prompt`, `Break`, `InstructionsApplyToFiles`, `Run`).
11+
// - The inner types (`Prompt`, `Break`, etc.) also derive `former::Former`.
12+
// - Applies `#[derive(former::Former)]` to the `FunctionStep` enum.
13+
// - Includes shared test logic from `usecase1_only_test.rs`.
14+
// - The included tests call the derived static methods (e.g., `FunctionStep::prompt()`, `FunctionStep::r#break()`), use the returned subformers to set fields of the inner types, and call `.form()` on the subformers to get the final `FunctionStep` enum instance.
15+
// - Asserts that the resulting enum instances match manually constructed expected values. This verifies that the derived subformer starters correctly integrate with the inner types' formers.
1616

1717
#[allow(unused_imports)]
1818
use super::*;

module/core/former_meta/src/derive_former/field_attrs.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ pub struct FieldAttributes {
166166

167167
/// Excludes a field from standalone constructor arguments.
168168
pub former_ignore: AttributePropertyFormerIgnore,
169+
170+
/// Includes a field as an argument in standalone constructor functions.
171+
pub arg_for_constructor: AttributePropertyArgForConstructor,
169172
}
170173

171174
impl FieldAttributes {
@@ -269,6 +272,7 @@ impl FieldAttributes {
269272
AttributeSubformCollectionSetter::KEYWORD => result.assign(AttributeSubformCollectionSetter::from_meta(attr)?),
270273
AttributeSubformEntrySetter::KEYWORD => result.assign(AttributeSubformEntrySetter::from_meta(attr)?),
271274
AttributePropertyFormerIgnore::KEYWORD => result.assign(AttributePropertyFormerIgnore::from(true)),
275+
AttributePropertyArgForConstructor::KEYWORD => result.assign(AttributePropertyArgForConstructor::from(true)),
272276
_ => {} // Allow unknown attributes
273277
}
274278
}
@@ -344,6 +348,17 @@ where
344348
}
345349
}
346350

351+
impl<IntoT> Assign<AttributePropertyArgForConstructor, IntoT> for FieldAttributes
352+
where
353+
IntoT: Into<AttributePropertyArgForConstructor>,
354+
{
355+
#[inline(always)]
356+
fn assign(&mut self, component: IntoT) {
357+
let component = component.into();
358+
self.arg_for_constructor.assign(component);
359+
}
360+
}
361+
347362
// ==================================
348363
// Attribute Definitions
349364
// ==================================
@@ -1078,3 +1093,18 @@ impl AttributePropertyComponent for FormerIgnoreMarker {
10781093
/// Indicates whether a field should be excluded from standalone constructor arguments.
10791094
/// Defaults to `false`. Parsed as a singletone attribute (`#[former_ignore]`).
10801095
pub type AttributePropertyFormerIgnore = AttributePropertyOptionalSingletone<FormerIgnoreMarker>;
1096+
1097+
// =
1098+
1099+
/// Marker type for attribute property including a field as a constructor argument.
1100+
/// Defaults to `false`.
1101+
#[derive(Debug, Default, Clone, Copy)]
1102+
pub struct ArgForConstructorMarker;
1103+
1104+
impl AttributePropertyComponent for ArgForConstructorMarker {
1105+
const KEYWORD: &'static str = "arg_for_constructor";
1106+
}
1107+
1108+
/// Indicates whether a field should be included as an argument in standalone constructor functions.
1109+
/// Defaults to `false`. Parsed as a singletone attribute (`#[arg_for_constructor]`).
1110+
pub type AttributePropertyArgForConstructor = AttributePropertyOptionalSingletone<ArgForConstructorMarker>;

module/core/former_meta/src/derive_former/former_struct.rs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,20 @@ specific needs of the broader forming context. It mandates the implementation of
756756
// Identify fields marked as constructor arguments
757757
let constructor_args_fields: Vec<_> = formed_fields
758758
.iter()
759-
.filter( | f | !f.attrs.former_ignore.value( false ) ) // Use the parsed attribute
759+
.filter( | f | {
760+
// If #[former_ignore] is present, exclude the field
761+
if f.attrs.former_ignore.value(false) {
762+
false
763+
}
764+
// If #[arg_for_constructor] is present, include the field
765+
else if f.attrs.arg_for_constructor.value(false) {
766+
true
767+
}
768+
// Default behavior: include the field (inverted former_ignore logic)
769+
else {
770+
true
771+
}
772+
})
760773
.collect();
761774

762775
// Generate constructor function parameters
@@ -870,7 +883,14 @@ specific needs of the broader forming context. It mandates the implementation of
870883

871884
// Determine if all fields are constructor arguments
872885
// Note: We only consider fields that are part of the final struct (`formed_fields`)
873-
let all_fields_are_args = formed_fields.iter().all(|f| !f.attrs.former_ignore.value(false)); // Space around |
886+
let all_fields_are_args = formed_fields.iter().all(|f| {
887+
// Field is arg if it's not ignored AND (default behavior OR explicitly marked)
888+
if f.attrs.former_ignore.value(false) {
889+
false // Explicitly ignored
890+
} else {
891+
true // Default: include (or explicitly marked with arg_for_constructor)
892+
}
893+
}); // Space around |
874894

875895
// Determine return type and body based on Option 2 rule
876896
let (return_type, constructor_body) = if all_fields_are_args {
@@ -879,8 +899,20 @@ specific needs of the broader forming context. It mandates the implementation of
879899
let construction_args = formed_fields.iter().map(| f | // Space around |
880900
{
881901
let field_ident = f.ident;
882-
let param_name = ident::ident_maybe_raw( field_ident );
883-
quote! { #field_ident : #param_name.into() }
902+
// Check if this field is a constructor argument (same logic as filter above)
903+
let is_constructor_arg = if f.attrs.former_ignore.value(false) {
904+
false // Explicitly ignored
905+
} else {
906+
true // Default: include (or explicitly marked with arg_for_constructor)
907+
};
908+
909+
if is_constructor_arg {
910+
let param_name = ident::ident_maybe_raw( field_ident );
911+
quote! { #field_ident : #param_name.into() }
912+
} else {
913+
// Use default value for ignored fields
914+
quote! { #field_ident : ::core::default::Default::default() }
915+
}
884916
});
885917
let body = quote! { #struct_type_ref { #( #construction_args ),* } };
886918
(return_type, body)

0 commit comments

Comments
 (0)