feat(generator): implement Phase 1 DynamoMapper source generator with attribute-based mapping#7
feat(generator): implement Phase 1 DynamoMapper source generator with attribute-based mapping#7
Conversation
- Added a new project `DynamoMapper.SimpleExample` to demonstrate usage. - Updated solution file to include the new example project. - Added `.editorconfig` for consistent code formatting. - Updated `.gitignore` to ignore user-specific settings files.
… file consistency - Added `Directory.Packages.props` for centralized NuGet package version management. - Enabled `ManagePackageVersionsCentrally` and transitive pinning for all projects. - Simplified and standardized package references across all project files. - Added `ModuleInitializer.cs` to initialize VerifySourceGenerators for tests. - Adjusted `.csproj` files to remove version specifications where central management applies.
…nerator - Added `DynamoMapperGenerator` with a basic setup as an incremental source generator. - Updated project files to include `Microsoft.CodeAnalysis.Common` and `Microsoft.CodeAnalysis.CSharp`. - Enhanced test project with references for testing source generators and added `Verify.SourceGenerators`. - Refactored unit test to `SimpleVerifyTests` to include a basic source generator verification example. - Added `GeneratorTestHelpers` and `VerifyTestOptions` for consistent and reusable source generator test utilities. - Updated `Directory.Packages.props` with required dependencies for code analysis and verification tools.
…ibute mapping - Expanded `Program.cs` to include comprehensive DynamoDB `AttributeValue` mapping examples. - Added a POCO class (`ExampleEntity`) with properties demonstrating various type mappings. - Introduced enums (`Status`, `Priority`, `OrderStatus`) for example use cases. - Included `AWSSDK.DynamoDBv2` package reference in `DynamoMapper.SimpleExample.csproj`.
…ctionality - Introduced `MapperSyntaxProvider` for attribute-based class parsing and mapping extraction. - Added `GeneratorConstants` and `TrackingName` classes to manage constants and tracking names. - Updated `DynamoMapperGenerator` with logic for extracting and filtering mapping information. - Expanded project dependencies to include `Microsoft.CodeAnalysis.CSharp`. - Enhanced test helpers to support `DynamoMapper.Generator` namespace for testing.
- Added `DiagnosticInfo` and `MapperAndDiagnosticInfo` models for detailed error reporting and handling. - Implemented new extension methods for `IncrementalValueProvider` and `IEnumerable` to improve processing. - Created `MapperOutputGenerator` to streamline mapper code generation logic. - Updated `DynamoMapperGenerator` to include source output registrations for diagnostics and mappers. - Added `DiagnosticProvider` for building diagnostic information alongside mapper data. - Extended project references and dependencies for enhanced functionality and compatibility. - Improved `GeneratorTestHelpers` for more flexible and robust unit testing of source generators. - Enhanced tests to handle diagnostic outputs and additional mapper validations.
- Introduced `GeneratedCode` attribute to annotate generated mappers with generator metadata. - Utilized `Assembly.GetExecutingAssembly` to include generator name and version in the attribute.
…ions - Introduced `Pipe` method to streamline functional transformations. - Added `Mutate` method to apply in-place modifications and return the source.
…urce generation - Introduced `WellKnownTypes` for efficient retrieval and caching of known type symbols. - Added `BoundedCacheWithFactory` to manage a bounded weak-reference cache for analyzer performance. - Created `WellKnownTypeData` to define and map well-known types used in generator logic. - Incorporated a `THIRD-PARTY-LICENSES.txt` file to document and acknowledge third-party dependencies.
- Updated `MapperSyntaxProvider` to support parsing `ToItem` and `FromItem` mapper methods. - Ensured mapper methods follow consistent rules for POCO types and method signatures. - Integrated additional validation logic for `WellKnownTypes` with DynamoDB modeling support. - Refactored `MapperInfo` to include class metadata and model type for mapper generation. - Enhanced test cases to validate new mapper syntax rules and diagnostics.
…ception - Updated `MapperSyntaxProvider` to include namespace and method signatures in `MapperInfo`. - Added `GetMethodSignature` to extract detailed method signature display. - Refactored `MapperInfo` for improved metadata handling. - Introduced `DiagnosticException` to encapsulate diagnostic info and streamline error handling. - Enhanced `SimpleVerifyTests` with namespace inclusion for test cases. - Updated `DynamoMapper.SimpleExample` to include additional partial method declarations.
…odel details - Replaced `ClassName`, `ClassAccessibility`, and `ClassNamespace` with `MapperClassInfo`. - Introduced `ModelClassInfo` to encapsulate model type details in `MapperInfo`. - Updated `MapperSyntaxProvider` to reflect the restructured `MapperInfo` and return updated values. - Enhanced maintainability and clarity of mapper metadata representation.
…per generation - Introduced `MapperClassInfo`, `ModelClassInfo`, and related extensions for streamlined mapper generation. - Migrated mapper metadata extraction logic to `GeneratorContext` for improved modularity and reuse. - Introduced new extensions for `IncrementalValueProvider` and `IEnumerable` to enhance collection processing. - Refactored `MapperSyntaxProvider` to leverage new utilities and improve maintainability. - Added `ModelPropertyInfo` for handling model property metadata extraction. - Updated existing utilities to ensure compliance with naming conventions and extensibility.
…ity checks - Introduced `SymbolExtensions` to enhance property symbol analysis in source generation. - Added methods to check if a property is assignable from/to a type or both.
…s for `ExampleEntity` - Added `ToItem` method to map `ExampleEntity` properties to DynamoDB attribute values. - Introduced `FromItem` method to populate `ExampleEntity` from DynamoDB item attributes. - Utilized `CultureInfo.InvariantCulture` for consistent formatting of numeric and date-time values. - Enhanced support for nullable, enum, and complex types in serialization and deserialization. - Updated `DynamoMapper.SimpleExample` to showcase improved mapper functionality.
…or reporting - Introduced `Diagnostics` for centralized diagnostic descriptor creation. - Added `Result` and `DiagnosticResult` classes for structured result management. - Enhanced `SymbolExtensions` with assignability methods using `WellKnownType`. - Implemented `BuildFromItemMapping` in `ModelPropertyInfo` to support type-conversion validation. - Updated `WellKnownTypeData` to include `System.String` for type validation.
- Added `TemplateHelper` for loading, caching, and rendering Scriban templates. - Introduced `.scriban` templates for `ToItem` and `FromItem` mapper methods. - Updated `MapperEmitter` to utilize templates for code generation. - Enhanced error handling and validation for template-based mapper generation. - Refactored `MapperOutputGenerator` to `MapperEmitter` for semantic clarity.
- Refactored property symbol processing to consolidate null checks and optimize conditions. - Enhanced `BuildFromItemMapping` to support comprehensive type conversion diagnostics. - Introduced aggregation for successful mappings and diagnostics for improved clarity. - Updated nullable type mappings with explicit assignments for better generated code quality. - Added handling for nullable `TimeSpan` and improved switch-case consistency.
- Enhanced `Mapper.scriban` template to support null checks and streamlined method signatures. - Introduced `GeneratedCodeAttribute` in mapper templates for improved metadata accuracy. - Added `Diagnostics` to `MapperInfo` for centralized error and warning management. - Fixed property typo (`PropertieAssignments` to `PropertiedAssignments`) for consistency. - Updated nullable type mapping to include consistent comma placement in assignments. - Added utility extension for `List<T>.Add` method to facilitate chaining operations.
- Updated `Mapper.scriban` template to streamline partial method signatures. - Modified `MapperEmitter` to generate files based on mapper class names for clarity. - Enhanced test helpers with line scrubbing for consistent snapshot verification. - Added folder inclusion for snapshot management in test project. - Introduced `RegexHelper` with auto-generated regex support for attribute replacement.
…n mappings - Removed `ModelPropertyInfo` and its related extensions in favor of a more streamlined approach. - Added support for nullable enums in type mappings to enhance deserialization flexibility. - Updated test cases to verify comprehensive type handling, including nullable enums. - Modified `ExampleEntity` in the example project to reflect updated property definitions.
- Removed `DiagnosticsProvider` in favor of direct integration of diagnostic handling in `MapperEmitter`. - Streamlined `MapperClassInfo` by removing unused properties (`FullyQualifiedType`, `Accessibility`). - Introduced `MapperAndDiagnosticInfo` for combined mapper and diagnostic metadata representation. - Refined diagnostic descriptors in `Diagnostics` for clearer error and warning messages. - Consolidated diagnostic reporting and mapper generation logic in `DynamoMapperGenerator`.
…ue conversions - Refactored `AttributeValueExtensions` to improve readability using multi-line formatting. - Added `ToAttributeValue` and `ToNullableAttributeValue` extension methods for common types. - Introduced extensions for strings, booleans, numeric types, GUIDs, DateTime, and TimeSpan. - Used `CultureInfo.InvariantCulture` for consistent formatting across numeric and datetime values.
- Introduced `SetString` and `SetNullableString` for setting string values in the attribute dictionary. - Added `SetBool` and `SetNullableBool` to support boolean attribute manipulation. - Implemented setters for numeric types, including `SetInt`, `SetLong`, `SetFloat`, `SetDecimal`, and nullable variants. - Added support for `Guid`, `DateTime`, `DateTimeOffset`, and `TimeSpan` with nullable equivalents. - Enabled fluent chaining for attribute dictionary manipulations.
… enhance `ModelClassInfo` - Extended `Mapper.scriban` template with handling for properties in `ToItem` mapping generation. - Added `ToAttributeAssignments` to `ModelClassInfo` for enabling attribute value creation. - Implemented `BuildToItemMapping` for generating mappings to DynamoDB attribute values. - Refactored `CollectModelClassInfo` to aggregate diagnostics and mappings for both `ToItem` and `FromItem`. - Improved nullable and non-nullable type handling for consistent value transformations.
- Introduced `SetEnum` and `SetNullableEnum` for setting enum values in the attribute dictionary. - Added `ToAttributeValue` and `ToNullableAttributeValue` extensions for enums and nullable enums. - Enabled fluent chaining for enum attribute manipulations.
… simplify enum conversion - Updated `Mapper.scriban` to initialize dictionaries with calculated capacity for efficiency. - Modified `MapperEmitter` to pass `DictionaryCapacity` based on property assignments. - Simplified enum-to-attribute conversions by removing unnecessary `ToString()` calls.
- Added `Simple_InitProperty` and `Simple_NoSetter` snapshot tests for mapper generation. - Verified support for properties using `init` and ones with no setters in generated code. - Ensured consistency in `DynamoMapper` output for various property configurations
- Modified `Mapper.scriban` to remove semicolon after the namespace statement for compatibility. - Updated `MapperClassInfo` to include conditional namespace declaration based on its presence. - Added a new example mapper `ExampleEntityMapper2` with partial mapping methods to the example project.
…ntations - Consolidated `Set*` and `SetNullable*` methods into unified implementations. - Simplified parameter handling by leveraging nullable type capabilities. - Updated XML documentation to reflect the new method signatures.
- Consolidated `SetNullable*` methods into their respective `Set*` counterparts in generated code. - Updated enum mapping to support nullable enums with default values in templates. - Enhanced `PropertyInfo` to efficiently handle nullable and non-nullable type distinctions. - Simplified attribute dictionary initialization and population for better readability. - Removed redundant `GetNullableEnum` method from `AttributeValueExtensions`.
…etNullableString` - Added `requiredness` parameter to `GetString` and `GetNullableString` for better nullability control. - Updated both methods to handle DynamoDB NULL values more consistently. - Introduced utility methods `GetNullableValue` and `TryGetValue` for streamlined attribute access. - Updated XML documentation to reflect new method signatures and behavioral changes.
- Added `requiredness` parameter to all getter methods for fine-grained nullability control. - Replaced redundant method bodies with expression-bodied members for cleaner implementation. - Standardized handling of `IsNotNull` and `IsNull` checks across attribute processing methods. - Updated XML documentation to reflect new method signatures and clarify behavior. - Improved parsing logic for numeric, boolean, `Guid`, and `DateTime` attributes using culture-specific settings. - Refactored nullable value handling to use `Map` utility for concise and reusable code.
…generation - Introduced `requiredness` to support fine-grained control of property nullability in mappers. - Added `omitEmptyStrings` and `omitNullStrings` options to streamline property assignment logic. - Updated `Get` and `Set` methods in generated code to integrate new options for improved flexibility.
…ndling - Refactored `GetMethodSignature` to consistently handle nullable parameter names and types. - Added `ToMethodParameterName` and `FromMethodParameterName` properties to `MapperOptions`. - Updated `PropertyInfo` to dynamically use context-defined parameter names. - Improved error handling in `EnsurePocoTypesMatch` for clearer diagnostic results. - Enhanced type symbol extensions to support nullable name formatting.
…tion - Replaced full method body with an expression-bodied member for conciseness. - Streamlined type checks and improved readability by removing intermediate return statements. - Ensured consistent style in type validation logic. refactor(generator): replace `Map` with `Tap` for method parameter handling - Updated usage of `Map` to `Tap` in method parameter assignments for consistency. - Refined parameter handling logic for better readability and alignment with project conventions.
…neration - Added diagnostics for invalid mapper methods to improve error visibility. - Introduced implicit operator for `DiagnosticResult` to simplify result creation. - Refactored `PropertyInfo` generation to extract nullable type logic into `GetProps`. - Enhanced property mapping with support for fine-grained options like `requiredness`, `omitEmptyStrings`, and `omitNullStrings`. - Improved handling of nullable enums and other complex types for better mapper accuracy.
- Introduced `GetEnum` and `GetNullableEnum` with support for custom formats in parsing. - Added `SetEnum` method to handle formatted enum value assignments. - Enhanced XML documentation to detail supported formats and usage scenarios. - Refactored existing enum handling logic for consistency and clarity.
…quiredness` - Introduced `Set*` methods for numeric, DateTime, TimeSpan, Guid, and string attributes. - Enhanced all `Get*` methods to support the `Requiredness` parameter for fine-grained nullability control. - Added extensive new test cases for both `Set*` and `Get*` methods to validate behaviors. - Improved handling of DynamoDB `NULL`, optional attributes, and missing keys across all types. - Updated XML documentation to reflect updated method signatures and new behaviors.
…an up project file - Replaced `<None>` reference with `<AnalyzerDependency>` for `Humanizer.dll` configuration. - Commented out unused `None` configuration for better clarity and upkeep. - Removed redundant `<PropertyGroup>` entry disabling PolySharp's `ModuleInitializerAttribute` shim. - Refined `ItemGroup` formatting for `AdditionalFiles` to align with consistent style. - Adjusted spacing in `GetTargetPathDependsOn` property for better readability.
…lity - Updated `<None>` configuration for `Humanizer.Core` to enable packaging and specify visibility. - Added `Humanizer.Core` as a dependency in test project with appropriate settings. - Improved handling of analyzer dependencies and ensured consistent packaging behavior across projects.
ncipollina
left a comment
There was a problem hiding this comment.
Just left a few comments.
There was a problem hiding this comment.
🤔 I haven't decided, but I'm not sure I want this included in the repo.
There was a problem hiding this comment.
Wellll. I did just removed the stuff from it that was leftover from my project
| - '.github/workflows/docs.yml' | ||
| pull_request: | ||
| branches: [ main ] | ||
| types: [ opened, synchronize, reopened, ready_for_review ] |
There was a problem hiding this comment.
❗️ I don't think this change was necessary. It didn't actually generate new docs on PRs.
There was a problem hiding this comment.
I think I added this because it was not kicking off when it went from daft to ready for review.
examples/DynamoMapper.SimpleExample/DynamoMapper.SimpleExample.csproj
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
❓I've been thinking about this and unlike Mapperly, you can only map from an object to a Dictionary and back. Does it make sense to have Diagnostic Descriptiors to validate/ensure this?
There was a problem hiding this comment.
Ok, I messed around with this, and the problem is that the logic we use to determine what method we should be implementing is just returning a bool. If we want to return diagnostics for any methods that don't match our specific requirements, it could get messy. What if a user wanted another method on the mapper class? What are your thoughts on this?
There was a problem hiding this comment.
I'm ok w/ leaving as is, additional methods just really don't make a whole lot of sense.
There was a problem hiding this comment.
I agree. Maybe we add a story to come back and look at it again as an enhancement later?
- Removed detailed `<type>` and `<scope>` lists from PR template instructions for brevity. - Simplified guidance on PR title format to
- Changed `<LangVersion>` from `preview` to `14` in `DynamoMapper.SimpleExample.csproj`. - Ensures compatibility with stable language features in .NET 10.0 target framework.
- Updated `Map` method to support a generic return type for improved flexibility and reusability. - Simplified `FunctionalExtensions` by consolidating overloaded definitions.
…djustments - Replaced `HasSupportedSignature` with `IsToMethod` and `IsFromMethod` for better clarity. - Simplified `MapperClassInfo` logic by leveraging specific validation methods. - Changed `CreateWithDiagnostics` to private, restricting its accessibility. - Adjusted example project mapper methods to use private visibility where appropriate.
ncipollina
left a comment
There was a problem hiding this comment.
🚀 Nice work. Approved. Left a few comments/questions, but otherwise looks good to me.
src/LayeredCraft.DynamoMapper.Generators/Extensions/EnumerableExtensions.cs
Show resolved
Hide resolved
src/LayeredCraft.DynamoMapper.Generators/Extensions/IncrementalValueProviderExtensions.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
❓Does this need to be included?
There was a problem hiding this comment.
According to the docs here, it's needed. If I leave it out, I get compilation warnings.
There was a problem hiding this comment.
❓Does this need to be included?
- Deleted `WhereNotNull` extension for `IncrementalValuesProvider<T?>` where `T` is a struct. - Ensures alignment with updated project conventions and avoids redundancy in nullable handling.
- Deleted `WhereNotNull` extensions for nullable structs and classes in `EnumerableExtensions`. - Aligns with updated project conventions and removes duplication in nullable handling logic.
- Set `<EmbedUntrackedSources>` to true in `csproj` for better debugging support. - Modified generator DLLs to be packaged into `analyzers` and `lib/netstandard2.0` folders. - Updated `Runtime` project to set `<IsPackable>` to false, aligning with packaging conventions. - Added `taskfile.yaml` for automated tasks like cleaning, restoring, building, and packaging. - Enabled symbols and documentation generation in `Directory.Build.props`. - Updated `String` property in `SimpleExample` to `required` for null safety improvements.
… and configuration - Replaced `LayeredCraft.SourceGeneratorTools.Generator` with `LayeredCraft.SourceGeneratorTools`. - Updated project references to include generator DLLs for analyzers and netstandard2.0 libraries. - Adjusted `None` and `PackageReference` configurations for improved packaging consistency. - Refined `GetDependencyTargetPaths` to include SourceGeneratorTools DLL configurations. - Aligned test project file with updated `SourceGeneratorTools` packaging conventions.
There was a problem hiding this comment.
❓ What is this and why did you add it?
There was a problem hiding this comment.
Its like a makefile but better. Its just for automation of commands.
🚀 Pull Request
📋 Summary
This PR implements the foundational Phase 1 functionality of the DynamoMapper source generator, providing compile-time code generation for mapping between .NET domain models and DynamoDB AttributeValue dictionaries. The implementation follows a convention-first, attribute-based approach with zero runtime reflection.
Key Features:
IIncrementalGeneratorAPIToItemandFromItemmethodsArchitecture Highlights:
netstandard2.0) and runtime packagesResult<T>andDiagnosticInfotypesRecent Enhancements:
Dictionary<string, AttributeValue>types✅ Checklist
🧪 Testing
Snapshot Tests:
Example Project:
Productdomain modelTest Coverage:
📊 Impact
Lines Changed: ~4,700+ additions across 63 files
Key Files:
DynamoMapperGenerator.cs: Core incremental generator orchestrationMapperSyntaxProvider.cs: Roslyn syntax analysis and metadata extractionMapperClassInfo.cs&ModelClassInfo.cs: Mapper and model metadata structuresPropertyInfo.cs: Property-level configuration with requiredness and omit optionsAttributeValueExtensions.cs: 1,600+ lines of runtime helpers for type conversionsMapper.scriban: Template for generating ToItem/FromItem implementationsWellKnownTypes.cs: Cached type resolution for common .NET and AWS types🔍 Areas for Review
Critical Components:
Templates/Mapper.scriban): Review the generated code structure for ToItem/FromItem methodsModelClassInfo.cs,WellKnownTypes.cs): Verify type mapping logic covers all Phase 1 scenariosDiagnostics/): Ensure error messages are clear and actionableAttributeValueExtensions.cs): Check null handling, culture safety, and enum format supportDesign Decisions to Validate:
🚀 Next Steps (Future PRs)
Phase 1 Completion:
Phase 2 Planning:
💬 Notes for Reviewers
This PR represents the culmination of iterative development with 35+ commits refining the generator architecture, runtime helpers, and testing strategy. The implementation prioritizes:
The code is production-ready for Phase 1 scenarios (scalar type mapping with attribute-based configuration). All tests pass across net8.0, net9.0, and net10.0 target frameworks.
📝 Related Documentation