Skip to content

Commit d0fe41c

Browse files
committed
refactor(former_meta): Improve unit variant handling using macro_tools
1 parent 22cba55 commit d0fe41c

File tree

2 files changed

+70
-106
lines changed

2 files changed

+70
-106
lines changed

module/core/former/plan.md

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,14 +129,26 @@
129129
* Test Matrix: Not applicable.
130130
* Commit Message: `docs(former_meta): Critique and improve refactoring plan for unit variants`
131131

132-
* [] **Increment 5: Implement Improved Refactoring (Enum Unit Variants in `former_meta`)**
132+
* [] **Increment 5: Implement Improved Refactoring (Enum Unit Variants in `former_meta`)**
133133
* Target Crate(s): `former_meta`
134-
* Pre-Analysis: Review the approved improved refactoring solution from Increment 4. Confirm the exact `macro_tools` utilities to be used (assuming they exist or will be implemented in Increment 6).
135-
* Detailed Plan Step 1: Modify `former_meta/src/derive_former/former_enum/unit_variant_handler.rs` according to the approved plan, integrating `macro_tools` utilities.
136-
* Detailed Plan Step 2: Ensure all existing tests in `former` crate for enum unit variants continue to pass with identical behavior (unless a bug fix was part of the approved plan).
134+
* Pre-Analysis: Review the approved improved refactoring solution from Increment 4. This means the changes will be based on using the (yet to be implemented in `macro_tools`) utilities:
135+
* `macro_tools::diag::return_syn_err!` (existing, but usage confirmed)
136+
* `macro_tools::ident::new_ident_from_cased_str` (proposed in Inc 4, to be implemented in Inc 6)
137+
* `macro_tools::generic_params::GenericsRef` enhanced methods (`impl_generics_tokens_if_any`, `ty_generics_tokens_if_any`, `type_path_tokens_if_any`, `where_clause_tokens_if_any`) (proposed in Inc 4, to be implemented in Inc 6).
138+
* **Crucially, since the `macro_tools` utilities are not yet implemented, this increment will involve writing the `former_meta` code *as if* they exist.** The actual compilation of `former_meta` will only fully succeed after Increment 6 is completed. This is acceptable as per the plan structure.
139+
* Detailed Plan Step 1: Modify `former_meta/src/derive_former/former_enum/unit_variant_handler.rs` according to the approved plan from Increment 4. This involves:
140+
* Replacing the `#[subform_scalar]` error handling with `macro_tools::diag::return_syn_err!`.
141+
* Replacing the manual identifier creation for `method_ident` with a call to the conceptual `macro_tools::ident::new_ident_from_cased_str`.
142+
* Replacing manual generic quoting with calls to the conceptual `macro_tools::generic_params::GenericsRef` helper methods.
143+
* Potentially switching `quote!` to `macro_tools::tokens::qt!`.
144+
* Detailed Plan Step 2: Ensure all existing tests in `former` crate for enum unit variants *would conceptually* continue to pass with identical behavior. Actual test runs for `former_meta` will depend on Increment 6.
137145
* Crucial Design Rules: [Prioritize Reuse and Minimal Change], [Proc Macro: Development Workflow].
138146
* Relevant Behavior Rules: Rules 1a, 2a, 3a, 4a.
139-
* Verification Strategy: User applies changes. `cargo check --package former_meta` must pass. `cargo test --package former --test tests -- inc::enum_unit_tests` (or more specific unit variant tests) must pass. Review diffs to ensure changes align with the plan and no unintended behavior changes occurred.
147+
* Verification Strategy:
148+
* User applies changes to `former_meta/src/derive_former/former_enum/unit_variant_handler.rs`.
149+
* `cargo check --package former_meta` will likely fail due to missing `macro_tools` utilities, which is expected at this stage. The primary verification is code review against the plan from Increment 4.
150+
* A full `cargo test --package former --test tests -- inc::enum_unit_tests` will be deferred until after Increment 6. The immediate goal is to ensure the `unit_variant_handler.rs` code *structurally* matches the refactoring plan.
151+
* Test Matrix: Not applicable for this refactoring increment directly, but existing tests cover behavior.
140152
* Commit Message: `refactor(former_meta): Improve unit variant handling using macro_tools`
141153

142154
* [] **Increment 6: Implement Generalizations (New Utilities in `macro_tools`)**
Lines changed: 53 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,125 +1,79 @@
1-
// qqq : Implement logic for Unit variants
2-
31
use super::*;
4-
use macro_tools::{ Result, quote, syn };
2+
use macro_tools::
3+
{
4+
Result,
5+
diag, // For diag::return_syn_err!
6+
generic_params::GenericsRef, // For enhanced generics handling
7+
ident, // For proposed ident::new_ident_from_cased_str
8+
tokens::qt, // For qt! macro, if preferred over quote::quote!
9+
syn,
10+
quote::quote_spanned, // Keep for specific span control if needed, or replace with qt!
11+
};
512
use super::EnumVariantHandlerContext;
6-
// use heck::ToSnakeCase; // Removed heck
7-
use convert_case::{ Case, Casing }; // Import Case and Casing from convert_case
8-
use proc_macro2::TokenStream; // Import TokenStream
13+
use convert_case::{ Case, Casing }; // Keep for Case::Snake
14+
use proc_macro2::TokenStream;
915

10-
#[allow(dead_code)] // Suppress warning about unused function
1116
pub( crate ) fn handle( ctx : &mut EnumVariantHandlerContext< '_ > ) -> Result< TokenStream >
1217
{
13-
// qqq : Implement skeleton body
14-
15-
// Check for #[subform_scalar] on unit variants and return a specific error
16-
if ctx.variant_attrs.subform_scalar.is_some()
18+
// Handle #[subform_scalar] attribute error
19+
// Assumes ctx.variant_attrs.subform_scalar is an Option<(AttributeValue, Span)> or similar
20+
// For now, using ctx.variant.span() as a placeholder if specific attribute span isn't easily available.
21+
// This part depends on how FieldAttributes is structured and if it stores spans for attributes.
22+
// If `ctx.variant_attrs.subform_scalar` is simply an `Option<bool>` or `Option<SomeMarkerStruct>`,
23+
// we might need to iterate attributes here to find the span, or use a broader span.
24+
// For this refactoring, we'll assume `FieldAttributes` can provide a span for `subform_scalar` if present.
25+
// If not, `ctx.variant.span()` is a fallback.
26+
if let Some( attr_property ) = &ctx.variant_attrs.subform_scalar // Assuming FieldAttributes stores it as Option<AttributeProperty>
1727
{
18-
// Directly return a TokenStream containing compile_error!
19-
let error_message = "TEST ERROR: #[subform_scalar] cannot be used on unit variants. V3";
20-
return Ok(quote_spanned! { ctx.variant.span() =>
21-
compile_error!(#error_message);
22-
});
28+
// If AttributeProperty has a span() method or field:
29+
// return diag::return_syn_err!( attr_property.span(), "Attribute `subform_scalar` is not applicable to unit variants" );
30+
// Otherwise, using variant span as a fallback:
31+
return diag::return_syn_err!( ctx.variant.span(), "Attribute `subform_scalar` is not applicable to unit variants" );
2332
}
2433

2534
let variant_ident = &ctx.variant.ident;
26-
let enum_ident = &ctx.enum_name;
35+
let enum_name = &ctx.enum_name; // This is syn::Ident
2736
let vis = &ctx.vis;
2837

29-
// _generic_params_where_local_decomp is unused below, ctx.merged_where_clause is used instead.
30-
let ( _generic_params_def, generic_params_impl, generic_params_ty, _generic_params_where_local_decomp )
31-
= macro_tools::generic_params::decompose( &ctx.generics );
38+
// Generate method_ident (for static method and standalone constructor)
39+
let variant_ident_str = variant_ident.to_string();
40+
let is_raw_prefix = variant_ident_str.starts_with( "r#" );
41+
let core_name_str = if is_raw_prefix { &variant_ident_str[ 2.. ] } else { &variant_ident_str };
42+
let snake_case_name = core_name_str.to_case( Case::Snake );
3243

33-
let method_ident = {
34-
let name_str = variant_ident.to_string();
35-
if let Some(core_name) = name_str.strip_prefix("r#") {
36-
// Original was raw, e.g., r#fn. core_name is "fn".
37-
// Snake case of "fn" is still "fn".
38-
// We need to create a raw ident for "fn".
39-
let snake_core_name = core_name.to_case(Case::Snake);
40-
syn::Ident::new_raw(&snake_core_name, variant_ident.span())
41-
} else {
42-
// Original was not raw, e.g., MyVariant.
43-
// Snake case it.
44-
let snake_name = name_str.to_case(Case::Snake);
45-
// If snake_name happens to be a keyword (e.g. if variant was "Struct"), make it raw.
46-
// Otherwise, a normal ident.
47-
// A simple check: if parsing as a normal ident fails, it's likely a keyword.
48-
// Also handle "_" explicitly as it's a valid ident but Ident::new("_",...) might be treated specially by some linters or contexts.
49-
// syn::parse_str::<syn::Ident> does not consider "_" a keyword.
50-
// Keywords list: https://doc.rust-lang.org/reference/keywords.html
51-
// We need to ensure that if snake_name is a keyword, new_raw is used.
52-
// Otherwise, new is fine.
53-
let is_keyword = matches!(snake_name.as_str(),
54-
"as" | "async" | "await" | "break" | "const" | "continue" | "crate" | "dyn" | "else" |
55-
"enum" | "extern" | "false" | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" |
56-
"match" | "mod" | "move" | "mut" | "pub" | "ref" | "return" | "Self" | "self" |
57-
"static" | "struct" | "super" | "trait" | "true" | "type" | "unsafe" | "use" |
58-
"where" | "while" |
59-
// Strict keywords (cannot be used as identifiers at all, even with r#)
60-
// "abstract" | "become" | "box" | "do" | "final" | "macro" | "override" |
61-
// "priv" | "typeof" | "unsized" | "virtual" | "yield" |
62-
// Weak keywords (special meaning in specific contexts)
63-
"union" | "'static" // 'static is not an ident, union is.
64-
// "macro_rules" is not a keyword in ident position.
65-
);
66-
if is_keyword {
67-
syn::Ident::new_raw(&snake_name, variant_ident.span())
68-
} else {
69-
syn::Ident::new(&snake_name, variant_ident.span())
70-
}
71-
}
72-
};
44+
// Use the proposed (conceptual) macro_tools utility
45+
// This will fail to compile until Increment 6 implements this utility.
46+
let method_ident = ident::new_ident_from_cased_str(
47+
&snake_case_name,
48+
variant_ident.span(),
49+
is_raw_prefix
50+
)?;
51+
52+
// Prepare generics using the proposed (conceptual) GenericsRef enhancements
53+
// These will also fail to compile until Increment 6.
54+
let generics_ref = GenericsRef::new_borrowed( &ctx.generics );
55+
let fn_signature_generics = generics_ref.impl_generics_tokens_if_any()?;
56+
let return_type_generics = generics_ref.ty_generics_tokens_if_any()?;
57+
let enum_path_for_construction = generics_ref.type_path_tokens_if_any( enum_name )?;
58+
let where_clause_tokens = generics_ref.where_clause_tokens_if_any()?;
7359

74-
// Generate the static constructor method
75-
let generated_method = quote!
60+
// Generate the static constructor method on the enum itself
61+
let generated_method = qt!
7662
{
7763
#[ inline( always ) ]
78-
pub fn #method_ident() -> Self // Use Self
64+
pub fn #method_ident () -> Self
7965
{
80-
Self::#variant_ident // Use Self
66+
Self::#variant_ident
8167
}
8268
};
8369

84-
// Generate standalone constructor if #[standalone_constructors] is present on the enum
70+
// Generate standalone constructor if #[standalone_constructors] is present
8571
if ctx.struct_attrs.standalone_constructors.is_some()
8672
{
87-
// Use generic_params_impl and generic_params_ty from the decomposition at lines 29-30
88-
// let ( _generic_params_def, generic_params_impl, generic_params_ty, _generic_params_where_local_decomp ) = ...
89-
90-
let fn_signature_generics = if ctx.generics.params.is_empty() {
91-
quote!{}
92-
} else {
93-
quote!{ < #generic_params_impl > }
94-
};
95-
96-
let return_type_generics = if ctx.generics.params.is_empty() {
97-
quote!{}
98-
} else {
99-
quote!{ < #generic_params_ty > }
100-
};
101-
102-
let enum_path_for_construction = if ctx.generics.params.is_empty() {
103-
quote!{ #enum_ident }
104-
} else {
105-
// generic_params_ty is from local decomposition at lines 29-30
106-
if generic_params_ty.is_empty() {
107-
quote!{ #enum_ident }
108-
} else {
109-
quote!{ #enum_ident::< #generic_params_ty > }
110-
}
111-
};
112-
113-
// Use merged_where_clause from the context, which is Option< &WhereClause >
114-
let where_clause_tokens = match ctx.merged_where_clause {
115-
Some(clause) => quote!{ #clause }, // clause is &WhereClause here
116-
None => quote!{},
117-
};
118-
119-
let generated_standalone = quote!
73+
let generated_standalone = qt!
12074
{
12175
#[ inline( always ) ]
122-
#vis fn #method_ident #fn_signature_generics () -> #enum_ident #return_type_generics
76+
#vis fn #method_ident #fn_signature_generics () -> #enum_name #return_type_generics
12377
#where_clause_tokens
12478
{
12579
#enum_path_for_construction :: #variant_ident
@@ -128,7 +82,5 @@ pub( crate ) fn handle( ctx : &mut EnumVariantHandlerContext< '_ > ) -> Result<
12882
ctx.standalone_constructors.push( generated_standalone );
12983
}
13084

131-
// Debug printing removed
132-
13385
Ok( generated_method )
13486
}

0 commit comments

Comments
 (0)