From f3bff6ed7ea2b5ec1aa68b51aadd11b366dcfab4 Mon Sep 17 00:00:00 2001 From: Jonas Ha Date: Thu, 15 Jan 2026 08:30:26 -0500 Subject: [PATCH 1/2] feat(mapper): rename methods for consistency in naming conventions - Renamed methods to ensure clearer and standardized naming conventions. - Changed `ToItem` to `FromModel`, and `FromItem` to `ToModel`. - Updated related tests, documentation, and snapshots to reflect this change. - Adjusted parameter/property names and method signatures accordingly. --- CLAUDE.md | 18 +-- README.md | 8 +- docs/core-concepts/how-it-works.md | 56 +++---- docs/examples/single-table-design.md | 66 ++++---- docs/getting-started/installation.md | 4 +- docs/getting-started/quick-start.md | 10 +- docs/index.md | 15 +- docs/roadmap/phase-1.md | 124 ++++++++------- docs/roadmap/phase-2.md | 16 +- docs/usage/converters.md | 24 +-- docs/usage/customization-hooks.md | 146 +++++++++--------- docs/usage/static-converters.md | 22 +-- .../Program.cs | 6 +- .../DynamoMapper.SimpleExample/Program.cs | 6 +- .../Emitters/MapperEmitter.cs | 18 +-- .../GeneratorContext.cs | 12 +- .../Models/MapperClassInfo.cs | 77 ++++----- .../Models/MapperInfo.cs | 4 +- .../Models/PropertyInfo.cs | 2 +- .../Options/MapperOptions.cs | 4 +- .../Models/PropertyMappingSpec.cs | 8 +- .../PropertyMappingCodeRenderer.cs | 56 +++---- .../PropertyMappingSpecBuilder.cs | 72 ++++----- .../TypeMappingStrategyResolver.cs | 20 +-- .../Templates/Mapper.scriban | 14 +- .../IgnoreMapping.cs | 10 +- .../DynamoFieldVerifyTests.cs | 36 ++--- .../DynamoIgnoreVerifyTests.cs | 12 +- .../SimpleVerifyTests.cs | 42 ++--- ...stomType#ExampleEntityMapper.g.verified.cs | 4 +- ...omMethod#ExampleEntityMapper.g.verified.cs | 4 +- ...Override#ExampleEntityMapper.g.verified.cs | 4 +- ...Optional#ExampleEntityMapper.g.verified.cs | 4 +- ...Required#ExampleEntityMapper.g.verified.cs | 4 +- ...Override#ExampleEntityMapper.g.verified.cs | 4 +- ...Override#ExampleEntityMapper.g.verified.cs | 4 +- ...d_Simple#ExampleEntityMapper.g.verified.cs | 4 +- ...ToMethod#ExampleEntityMapper.g.verified.cs | 4 +- ...e_Simple#ExampleEntityMapper.g.verified.cs | 8 +- ...perTypes#ExampleEntityMapper.g.verified.cs | 4 +- ...ltValues#ExampleEntityMapper.g.verified.cs | 4 +- ...ptyModel#ExampleEntityMapper.g.verified.cs | 4 +- ...lloWorld#ExampleEntityMapper.g.verified.cs | 4 +- ...Property#ExampleEntityMapper.g.verified.cs | 4 +- ...fixWorks#ExampleEntityMapper.g.verified.cs | 4 +- ...NoSetter#ExampleEntityMapper.g.verified.cs | 4 +- ...ToMethod#ExampleEntityMapper.g.verified.cs | 2 +- ...oToMethod#ExampleMyDtoMapper.g.verified.cs | 2 +- ...romMethod#ExampleMyDtoMapper.g.verified.cs | 2 +- ...NoSetter#ExampleEntityMapper.g.verified.cs | 4 +- ...e_Record#ExampleEntityMapper.g.verified.cs | 4 +- 51 files changed, 507 insertions(+), 487 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 7c4938a..2a11507 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -76,7 +76,7 @@ dotnet run --project examples/DynamoMapper.SimpleExample/DynamoMapper.SimpleExam 2. **Analysis**: `MapperSyntaxProvider.Transformer()` extracts mapper and model metadata 3. **Type Resolution**: Analyzes properties to determine AttributeValue mappings 4. **Code Generation**: `MapperEmitter.Generate()` renders Scriban template -5. **Output**: Generates `{MapperClassName}.g.cs` with `ToItem` and `FromItem` methods +5. **Output**: Generates `{MapperClassName}.g.cs` with `FromModel` and `ToModel` methods ### Key Components @@ -94,7 +94,7 @@ dotnet run --project examples/DynamoMapper.SimpleExample/DynamoMapper.SimpleExam **WellKnownTypes**: Caches and resolves commonly-used types from Roslyn compilation -**Mapper.scriban**: Scriban template that generates both `ToItem` and `FromItem` method implementations +**Mapper.scriban**: Scriban template that generates both `FromModel` and `ToModel` method implementations ## Supported Types (Phase 1) @@ -128,8 +128,8 @@ Nullable: All nullable versions of the above ## Naming Conventions **Method Prefixes**: -- "To" prefix: Domain model → DynamoDB (e.g., `ToItem`, `ToProduct`) -- "From" prefix: DynamoDB → Domain model (e.g., `FromItem`, `FromProduct`) +- "To" prefix: Domain model → DynamoDB (e.g., `FromModel`, `ToProduct`) +- "From" prefix: DynamoDB → Domain model (e.g., `ToModel`, `FromProduct`) **Generator Tracking Names** (in `TrackingName.cs`): Used for Roslyn incremental generation debugging @@ -137,13 +137,13 @@ Nullable: All nullable versions of the above Mappers support four lifecycle hooks as `static partial void` methods: -- `BeforeToItem(T source, Dictionary item)`: Before property mapping -- `AfterToItem(T source, Dictionary item)`: After property mapping (add pk/sk here) -- `BeforeFromItem(Dictionary item)`: Before deserialization -- `AfterFromItem(Dictionary item, ref T entity)`: After object construction +- `BeforeFromModel(T source, Dictionary item)`: Before property mapping +- `AfterFromModel(T source, Dictionary item)`: After property mapping (add pk/sk here) +- `BeforeToModel(Dictionary item)`: Before deserialization +- `AfterToModel(Dictionary item, ref T entity)`: After object construction **Common Use Cases**: -- Adding single-table keys (pk/sk) in `AfterToItem` +- Adding single-table keys (pk/sk) in `AfterFromModel` - Adding discriminator fields (`recordType`, `entityType`) - Adding TTL attributes - Capturing unmapped attributes in an attribute bag diff --git a/README.md b/README.md index 6e91ab1..9f86bbc 100644 --- a/README.md +++ b/README.md @@ -66,9 +66,9 @@ namespace MyApp.Data; public static partial class ProductMapper { [DynamoField(nameof(Product.Description), OmitIfNullOrWhiteSpace = true)] - public static partial Dictionary ToItem(Product source); + public static partial Dictionary FromModel(Product source); - public static partial Product FromItem(Dictionary item); + public static partial Product ToModel(Dictionary item); } ``` @@ -87,7 +87,7 @@ var product = new Product }; // Convert to DynamoDB item -var item = ProductMapper.ToItem(product); +var item = ProductMapper.FromModel(product); await dynamoDbClient.PutItemAsync(new PutItemRequest { TableName = "MyTable", @@ -104,7 +104,7 @@ var getResponse = await dynamoDbClient.GetItemAsync(new GetItemRequest ["sk"] = new AttributeValue { S = $"PRODUCT#{product.ProductId}" } } }); -var retrievedProduct = ProductMapper.FromItem(getResponse.Item); +var retrievedProduct = ProductMapper.ToModel(getResponse.Item); ``` For more examples including single-table patterns and custom converters, see the [Quick Start Guide](https://layeredcraft.github.io/dynamo-mapper/getting-started/quick-start/). diff --git a/docs/core-concepts/how-it-works.md b/docs/core-concepts/how-it-works.md index b231fec..c142a57 100644 --- a/docs/core-concepts/how-it-works.md +++ b/docs/core-concepts/how-it-works.md @@ -24,11 +24,11 @@ DynamoMapper is built on three fundamental principles: DynamoMapper supports **exactly two mapping directions**: ```csharp -// ToItem: Domain model → DynamoDB item -Dictionary ToItem(T source); +// FromModel: Domain model → DynamoDB item +Dictionary FromModel(T source); -// FromItem: DynamoDB item → Domain model -T FromItem(Dictionary item); +// ToModel: DynamoDB item → Domain model +T ToModel(Dictionary item); ``` ### What DynamoMapper Does NOT Do @@ -51,7 +51,7 @@ DynamoMapper uses .NET's `IIncrementalGenerator` API to analyze your code at com 1. **Discovery Phase** - Locate classes marked with `[DynamoMapper]` - - Find partial mapping methods (`ToItem`, `FromItem`) + - Find partial mapping methods (`FromModel`, `ToModel`) - Collect configuration attributes 2. **Analysis Phase** @@ -61,8 +61,8 @@ DynamoMapper uses .NET's `IIncrementalGenerator` API to analyze your code at com - Validate converters and hooks 3. **Code Generation Phase** - - Generate `ToItem` implementation - - Generate `FromItem` implementation + - Generate `FromModel` implementation + - Generate `ToModel` implementation - Emit diagnostics for configuration errors 4. **Compilation Phase** @@ -80,12 +80,12 @@ using Amazon.DynamoDBv2.Model; public static partial class ProductMapper { // Partial method declarations (you provide) - public static partial Dictionary ToItem(Product source); - public static partial Product FromItem(Dictionary item); + public static partial Dictionary FromModel(Product source); + public static partial Product ToModel(Dictionary item); // Generated implementations (DynamoMapper provides) - // - ToItem implementation - // - FromItem implementation + // - FromModel implementation + // - ToModel implementation } ``` @@ -110,8 +110,8 @@ public class Product [DynamoMapper(Convention = DynamoNamingConvention.CamelCase)] public static partial class ProductMapper { - public static partial Dictionary ToItem(Product source); - public static partial Product FromItem(Dictionary item); + public static partial Dictionary FromModel(Product source); + public static partial Product ToModel(Dictionary item); } ``` @@ -120,7 +120,7 @@ DynamoMapper generates: ```csharp public static partial class ProductMapper { - public static partial Dictionary ToItem(Product source) + public static partial Dictionary FromModel(Product source) { var item = new Dictionary(capacity: 3); @@ -131,7 +131,7 @@ public static partial class ProductMapper return item; } - public static partial Product FromItem(Dictionary item) + public static partial Product ToModel(Dictionary item) { var entity = new Product { @@ -170,7 +170,7 @@ DynamoMapper uses a layered configuration model: ```csharp [DynamoField(nameof(Product.Name), Required = true)] [DynamoField(nameof(Product.Description), OmitIfNullOrWhiteSpace = true)] -public static partial Dictionary ToItem(Product source); +public static partial Dictionary FromModel(Product source); ``` ### 3. Converters @@ -190,7 +190,7 @@ Two approaches, both first-class: ### 4. Customization Hooks ```csharp -static partial void AfterToItem(Product source, Dictionary item) +static partial void AfterFromModel(Product source, Dictionary item) { item["pk"] = new AttributeValue { S = $"PRODUCT#{source.ProductId}" }; item["sk"] = new AttributeValue { S = "METADATA" }; @@ -267,16 +267,16 @@ Hooks enable DynamoDB-specific patterns without compromising the focused mapping ```csharp // Before property mapping -static partial void BeforeToItem(Product source, Dictionary item); +static partial void BeforeFromModel(Product source, Dictionary item); // After property mapping - most common -static partial void AfterToItem(Product source, Dictionary item); +static partial void AfterFromModel(Product source, Dictionary item); // Before deserialization -static partial void BeforeFromItem(Dictionary item); +static partial void BeforeToModel(Dictionary item); // After object construction -static partial void AfterFromItem(Dictionary item, ref Product entity); +static partial void AfterToModel(Dictionary item, ref Product entity); ``` **Common use cases:** @@ -331,11 +331,11 @@ error DM0201: Static conversion method 'ToStatus' not found on mapper 'OrderMapp ### Benchmarks (Typical) -| Operation | Time | Allocations | -|-----------|------|-------------| -| ToItem (5 properties) | ~50ns | 1 (dictionary) | -| FromItem (5 properties) | ~100ns | 1 (entity) | -| With hooks | +5-10ns | 0 additional | +| Operation | Time | Allocations | +|--------------------------|---------|----------------| +| FromModel (5 properties) | ~50ns | 1 (dictionary) | +| ToModel (5 properties) | ~100ns | 1 (entity) | +| With hooks | +5-10ns | 0 additional | ## Design Constraints @@ -382,8 +382,8 @@ Phase 2 adds an optional fluent DSL while maintaining all Phase 1 capabilities: [DynamoMapper] public static partial class ProductMapper { - public static partial Dictionary ToItem(Product source); - public static partial Product FromItem(Dictionary item); + public static partial Dictionary FromModel(Product source); + public static partial Product ToModel(Dictionary item); static partial void Configure(DynamoMapBuilder map) { diff --git a/docs/examples/single-table-design.md b/docs/examples/single-table-design.md index d84a84a..0e1d827 100644 --- a/docs/examples/single-table-design.md +++ b/docs/examples/single-table-design.md @@ -51,10 +51,10 @@ using Amazon.DynamoDBv2.Model; [DynamoMapper(Convention = DynamoNamingConvention.CamelCase)] public static partial class CustomerMapper { - public static partial Dictionary ToItem(Customer source); - public static partial Customer FromItem(Dictionary item); + public static partial Dictionary FromModel(Customer source); + public static partial Customer ToModel(Dictionary item); - static partial void AfterToItem(Customer source, Dictionary item) + static partial void AfterFromModel(Customer source, Dictionary item) { // PK/SK for customer entity item["pk"] = new AttributeValue { S = $"CUSTOMER#{source.CustomerId}" }; @@ -69,7 +69,7 @@ public static partial class CustomerMapper item["gsi1sk"] = new AttributeValue { S = $"CUSTOMER#{source.CustomerId}" }; } - static partial void BeforeFromItem(Dictionary item) + static partial void BeforeToModel(Dictionary item) { // Validate entity type before mapping if (item.TryGetValue("entityType", out var typeAttr) && typeAttr.S != "Customer") @@ -89,11 +89,11 @@ public static partial class CustomerMapper public static partial class OrderMapper { [DynamoField(nameof(Order.Status), Converter = typeof(OrderStatusConverter))] - public static partial Dictionary ToItem(Order source); + public static partial Dictionary FromModel(Order source); - public static partial Order FromItem(Dictionary item); + public static partial Order ToModel(Dictionary item); - static partial void AfterToItem(Order source, Dictionary item) + static partial void AfterFromModel(Order source, Dictionary item) { // PK/SK for order under customer item["pk"] = new AttributeValue { S = $"CUSTOMER#{source.CustomerId}" }; @@ -112,7 +112,7 @@ public static partial class OrderMapper item["gsi2sk"] = new AttributeValue { S = source.CreatedAt.ToString("O") }; } - static partial void BeforeFromItem(Dictionary item) + static partial void BeforeToModel(Dictionary item) { if (item.TryGetValue("entityType", out var typeAttr) && typeAttr.S != "Order") { @@ -165,7 +165,7 @@ var request = new GetItemRequest }; var response = await dynamoDb.GetItemAsync(request); -var customer = CustomerMapper.FromItem(response.Item); +var customer = CustomerMapper.ToModel(response.Item); ``` ### 2. Get All Orders for Customer @@ -183,7 +183,7 @@ var request = new QueryRequest }; var response = await dynamoDb.QueryAsync(request); -var orders = response.Items.Select(OrderMapper.FromItem).ToList(); +var orders = response.Items.Select(OrderMapper.ToModel).ToList(); ``` ### 3. Get Customer by Email (GSI1) @@ -201,7 +201,7 @@ var request = new QueryRequest }; var response = await dynamoDb.QueryAsync(request); -var customer = CustomerMapper.FromItem(response.Items.First()); +var customer = CustomerMapper.ToModel(response.Items.First()); ``` ### 4. Get Orders by Status (GSI1) @@ -219,7 +219,7 @@ var request = new QueryRequest }; var response = await dynamoDb.QueryAsync(request); -var orders = response.Items.Select(OrderMapper.FromItem).ToList(); +var orders = response.Items.Select(OrderMapper.ToModel).ToList(); ``` ## Advanced Patterns @@ -238,10 +238,10 @@ public class Session [DynamoMapper(Convention = DynamoNamingConvention.CamelCase)] public static partial class SessionMapper { - public static partial Dictionary ToItem(Session source); - public static partial Session FromItem(Dictionary item); + public static partial Dictionary FromModel(Session source); + public static partial Session ToModel(Dictionary item); - static partial void AfterToItem(Session source, Dictionary item) + static partial void AfterFromModel(Session source, Dictionary item) { // Session keys item["pk"] = new AttributeValue { S = $"USER#{source.UserId}" }; @@ -286,10 +286,10 @@ public class ProductReview [DynamoMapper(Convention = DynamoNamingConvention.CamelCase)] public static partial class ProductMapper { - public static partial Dictionary ToItem(Product source); - public static partial Product FromItem(Dictionary item); + public static partial Dictionary FromModel(Product source); + public static partial Product ToModel(Dictionary item); - static partial void AfterToItem(Product source, Dictionary item) + static partial void AfterFromModel(Product source, Dictionary item) { // Product hierarchy under category item["pk"] = new AttributeValue { S = $"CATEGORY#{source.CategoryId}" }; @@ -305,10 +305,10 @@ public static partial class ProductMapper [DynamoMapper(Convention = DynamoNamingConvention.CamelCase)] public static partial class ProductReviewMapper { - public static partial Dictionary ToItem(ProductReview source); - public static partial ProductReview FromItem(Dictionary item); + public static partial Dictionary FromModel(ProductReview source); + public static partial ProductReview ToModel(Dictionary item); - static partial void AfterToItem(ProductReview source, Dictionary item) + static partial void AfterFromModel(ProductReview source, Dictionary item) { // Reviews under product item["pk"] = new AttributeValue { S = $"PRODUCT#{source.ProductId}" }; @@ -345,12 +345,12 @@ public class Product public static partial class ProductMapper { [DynamoIgnore(nameof(Product.Metadata))] - public static partial Dictionary ToItem(Product source); + public static partial Dictionary FromModel(Product source); [DynamoIgnore(nameof(Product.Metadata))] - public static partial Product FromItem(Dictionary item); + public static partial Product ToModel(Dictionary item); - static partial void AfterToItem(Product source, Dictionary item) + static partial void AfterFromModel(Product source, Dictionary item) { // Single-table keys item["pk"] = new AttributeValue { S = $"PRODUCT#{source.ProductId}" }; @@ -367,7 +367,7 @@ public static partial class ProductMapper } } - static partial void AfterFromItem(Dictionary item, ref Product entity) + static partial void AfterToModel(Dictionary item, ref Product entity) { // Capture unmapped attributes var coreAttributes = new HashSet @@ -399,10 +399,10 @@ public class Product [DynamoMapper(Convention = DynamoNamingConvention.CamelCase)] public static partial class ProductMapper { - public static partial Dictionary ToItem(Product source); - public static partial Product FromItem(Dictionary item); + public static partial Dictionary FromModel(Product source); + public static partial Product ToModel(Dictionary item); - static partial void AfterToItem(Product source, Dictionary item) + static partial void AfterFromModel(Product source, Dictionary item) { // Base keys item["pk"] = new AttributeValue { S = $"PRODUCT#{source.ProductId}" }; @@ -453,8 +453,8 @@ var results = response.Items.Select(item => var entityType = item["entityType"].S; return entityType switch { - "Customer" => (object)CustomerMapper.FromItem(item), - "Order" => (object)OrderMapper.FromItem(item), + "Customer" => (object)CustomerMapper.ToModel(item), + "Order" => (object)OrderMapper.ToModel(item), _ => throw new InvalidOperationException($"Unknown entity type: {entityType}") }; }).ToList(); @@ -478,11 +478,11 @@ public class Order public static partial class OrderMapper { [DynamoField(nameof(Order.Status), Converter = typeof(OrderStatusConverter))] - public static partial Dictionary ToItem(Order source); + public static partial Dictionary FromModel(Order source); - public static partial Order FromItem(Dictionary item); + public static partial Order ToModel(Dictionary item); - static partial void AfterToItem(Order source, Dictionary item) + static partial void AfterFromModel(Order source, Dictionary item) { item["pk"] = new AttributeValue { S = $"CUSTOMER#{source.CustomerId}" }; item["sk"] = new AttributeValue { S = $"ORDER#{source.OrderId}" }; diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index 2236a47..97b5a56 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -41,8 +41,8 @@ using Amazon.DynamoDBv2.Model; [DynamoMapper] public static partial class TestMapper { - public static partial Dictionary ToItem(TestEntity source); - public static partial TestEntity FromItem(Dictionary item); + public static partial Dictionary FromModel(TestEntity source); + public static partial TestEntity ToModel(Dictionary item); } public class TestEntity diff --git a/docs/getting-started/quick-start.md b/docs/getting-started/quick-start.md index 63032ce..288081d 100644 --- a/docs/getting-started/quick-start.md +++ b/docs/getting-started/quick-start.md @@ -36,9 +36,9 @@ namespace MyApp.Data; public static partial class OrderMapper { [DynamoField(nameof(Order.Notes), OmitIfNullOrWhiteSpace = true)] - public static partial Dictionary ToItem(Order source); + public static partial Dictionary FromModel(Order source); - public static partial Order FromItem(Dictionary item); + public static partial Order ToModel(Dictionary item); } ``` @@ -55,7 +55,7 @@ var order = new Order }; // Convert to DynamoDB item -var item = OrderMapper.ToItem(order); +var item = OrderMapper.FromModel(order); // Save to DynamoDB await dynamoDbClient.PutItemAsync(new PutItemRequest @@ -76,14 +76,14 @@ var response = await dynamoDbClient.GetItemAsync(new GetItemRequest }); // Convert back to domain model -var retrievedOrder = OrderMapper.FromItem(response.Item); +var retrievedOrder = OrderMapper.ToModel(response.Item); ``` ## What Just Happened? 1. **`[DynamoMapper]`** - Marks the mapper and sets CamelCase naming (OrderId → orderId) 2. **`[DynamoField]`** - Configured Notes field to omit if null/whitespace -3. **Generated Code** - DynamoMapper generated ToItem and FromItem implementations at compile time +3. **Generated Code** - DynamoMapper generated FromModel and ToModel implementations at compile time ## Next Steps diff --git a/docs/index.md b/docs/index.md index 6c64581..bcf9f70 100644 --- a/docs/index.md +++ b/docs/index.md @@ -9,14 +9,15 @@ DynamoMapper is a .NET incremental source generator that generates high-performance mapping code between **domain models** and **Amazon DynamoDB `AttributeValue` dictionaries**. Using compile-time code generation, it eliminates runtime reflection, reduces allocations, and provides type-safe mapping for single-table DynamoDB patterns. **Important:** DynamoMapper is a **DynamoDB-specific mapping library**, not a general-purpose object mapper. It supports only two mapping directions: -- `T → Dictionary` (ToItem) -- `Dictionary → T` (FromItem) + +- `T → Dictionary` (FromModel) +- `Dictionary → T` (ToModel) Unlike general-purpose mappers like Mapperly or AutoMapper, DynamoMapper focuses exclusively on DynamoDB attribute mapping and single-table design patterns. ### Why DynamoMapper? -Writing manual `ToItem()` and `FromItem()` methods for DynamoDB is repetitive and error-prone: +Writing manual `FromModel()` and `ToModel()` methods for DynamoDB is repetitive and error-prone: - Field naming inconsistencies (`UserId` vs `userId`) - Attribute type mistakes (`S` vs `N`) @@ -44,13 +45,13 @@ public class Product public static partial class ProductMapper { [DynamoField(nameof(Product.Description), OmitIfNullOrWhiteSpace = true)] - public static partial Dictionary ToItem(Product source); - public static partial Product FromItem(Dictionary item); + public static partial Dictionary FromModel(Product source); + public static partial Product ToModel(Dictionary item); } // Usage - simple and type-safe -var item = ProductMapper.ToItem(product); -var product = ProductMapper.FromItem(item); +var item = ProductMapper.FromModel(product); +var product = ProductMapper.ToModel(item); ``` See the [Quick Start Guide](getting-started/quick-start.md) for a complete tutorial. diff --git a/docs/roadmap/phase-1.md b/docs/roadmap/phase-1.md index 224bb41..a24b453 100644 --- a/docs/roadmap/phase-1.md +++ b/docs/roadmap/phase-1.md @@ -16,7 +16,8 @@ description: Attribute-based mapping for DynamoMapper - comprehensive Phase 1 sp ## 1. Background and Goals ### 1.1 Problem Statement -Manually writing `ToItem()` and `FromItem()` methods for DynamoDB is repetitive and error-prone: + +Manually writing `FromModel()` and `ToModel()` methods for DynamoDB is repetitive and error-prone: - Field naming drift (`OwnerId` vs `ownerId`) - `S` vs `N` (string vs number) mistakes - Inconsistent null handling (omit vs store empty) @@ -90,9 +91,9 @@ namespace MyApp.Persistence; [DynamoMapper(Convention = DynamoNamingConvention.CamelCase)] public static partial class JediCharacterMapper { - public static partial Dictionary ToItem(JediCharacter source); + public static partial Dictionary FromModel(JediCharacter source); - public static partial JediCharacter FromItem(Dictionary item); + public static partial JediCharacter ToModel(Dictionary item); } ``` @@ -116,9 +117,9 @@ public static partial class JediCharacterMapper [DynamoIgnore(nameof(JediCharacter.CriticalHitChance))] [DynamoIgnore(nameof(JediCharacter.HitRoll))] [DynamoIgnore(nameof(JediCharacter.DamRoll))] - public static partial Dictionary ToItem(JediCharacter source); + public static partial Dictionary FromModel(JediCharacter source); - public static partial JediCharacter FromItem(Dictionary item); + public static partial JediCharacter ToModel(Dictionary item); } ``` @@ -237,8 +238,9 @@ Phase 1 must support omission policies: - `OmitIfDefault` *(optional; good for ints if desired)* If a property is omitted and is **Required**, generated code must: -- **not omit** on ToItem -- and **throw** on FromItem if missing + +- **not omit** on FromModel +- and **throw** on ToModel if missing --- @@ -329,7 +331,7 @@ public class OrderStatusConverter : IDynamoConverter Per field via attribute: ```csharp [DynamoField(nameof(Order.Status), Converter = typeof(OrderStatusConverter))] -public static partial Dictionary ToItem(Order source); +public static partial Dictionary FromModel(Order source); ``` Constraints: @@ -367,9 +369,9 @@ static TProperty FromMethodName(AttributeValue value); public static partial class OrderMapper { [DynamoField(nameof(Order.Status), ToMethod = nameof(ToOrderStatus), FromMethod = nameof(FromOrderStatus))] - public static partial Dictionary ToItem(Order source); + public static partial Dictionary FromModel(Order source); - public static partial Order FromItem(Dictionary item); + public static partial Order ToModel(Dictionary item); // Static conversion methods static AttributeValue ToOrderStatus(OrderStatus status) @@ -459,16 +461,18 @@ Generated code must: - Avoid reflection - Avoid `ToString()` without culture for numbers -### 10.2 `ToItem` Generation Requirements -Generated `ToItem` must: +### 10.2 `FromModel` Generation Requirements + +Generated `FromModel` must: - Instantiate a `Dictionary` with a sensible initial capacity if determinable - Set mapped fields using configured naming - Omit fields based on omission policy (unless required) - Call user hooks (if defined) **at deterministic points** (see §11) - Return the dictionary -### 10.3 `FromItem` Generation Requirements -Generated `FromItem` must: +### 10.3 `ToModel` Generation Requirements + +Generated `ToModel` must: - Validate required fields (throw `DynamoMappingException` with detailed message) - Read each field using `AttributeValue` kinds or converter as configured - Create target object via object initializer: @@ -496,11 +500,12 @@ Customization hooks are **first-class extension points** that allow developers t Phase 1 supports four lifecycle hooks that provide access to mapping operations at key points: -#### 11.1.1 BeforeToItem Hook -Invoked **before** property mapping during `ToItem`. +#### 11.1.1 BeforeFromModel Hook + +Invoked **before** property mapping during `FromModel`. ```csharp -static partial void BeforeToItem(T source, Dictionary item); +static partial void BeforeFromModel(T source, Dictionary item); ``` **Parameters:** @@ -512,11 +517,12 @@ static partial void BeforeToItem(T source, Dictionary it - Add metadata before property mapping - Pre-compute derived values -#### 11.1.2 AfterToItem Hook -Invoked **after** property mapping during `ToItem`. +#### 11.1.2 AfterFromModel Hook + +Invoked **after** property mapping during `FromModel`. ```csharp -static partial void AfterToItem(T source, Dictionary item); +static partial void AfterFromModel(T source, Dictionary item); ``` **Parameters:** @@ -530,11 +536,12 @@ static partial void AfterToItem(T source, Dictionary ite - Merge additional attribute dictionaries - Override or remove generated attributes -#### 11.1.3 BeforeFromItem Hook -Invoked **before** property mapping during `FromItem`. +#### 11.1.3 BeforeToModel Hook + +Invoked **before** property mapping during `ToModel`. ```csharp -static partial void BeforeFromItem(Dictionary item); +static partial void BeforeToModel(Dictionary item); ``` **Parameters:** @@ -546,11 +553,12 @@ static partial void BeforeFromItem(Dictionary item); - Extract and store unmapped attributes - Log or audit incoming data -#### 11.1.4 AfterFromItem Hook -Invoked **after** property mapping and object construction during `FromItem`. +#### 11.1.4 AfterToModel Hook + +Invoked **after** property mapping and object construction during `ToModel`. ```csharp -static partial void AfterFromItem(Dictionary item, ref T entity); +static partial void AfterToModel(Dictionary item, ref T entity); ``` **Parameters:** @@ -573,11 +581,11 @@ Hooks must be declared in the **same partial mapper class** as the mapping metho [DynamoMapper(Convention = DynamoNamingConvention.CamelCase)] public static partial class ProductMapper { - public static partial Dictionary ToItem(Product source); - public static partial Product FromItem(Dictionary item); + public static partial Dictionary FromModel(Product source); + public static partial Product ToModel(Dictionary item); // Hooks defined in the same partial class - static partial void AfterToItem(Product source, Dictionary item) + static partial void AfterFromModel(Product source, Dictionary item) { item["pk"] = new AttributeValue { S = $"PRODUCT#{source.ProductId}" }; item["sk"] = new AttributeValue { S = $"METADATA" }; @@ -594,21 +602,21 @@ Hooks may be placed in **separate partial class files** for better organization: [DynamoMapper(Convention = DynamoNamingConvention.CamelCase)] public static partial class ProductMapper { - public static partial Dictionary ToItem(Product source); - public static partial Product FromItem(Dictionary item); + public static partial Dictionary FromModel(Product source); + public static partial Product ToModel(Dictionary item); } // ProductMapper.Hooks.cs public static partial class ProductMapper { - static partial void AfterToItem(Product source, Dictionary item) + static partial void AfterFromModel(Product source, Dictionary item) { item["pk"] = new AttributeValue { S = $"PRODUCT#{source.ProductId}" }; item["sk"] = new AttributeValue { S = $"METADATA" }; item["recordType"] = new AttributeValue { S = "Product" }; } - static partial void AfterFromItem(Dictionary item, ref Product entity) + static partial void AfterToModel(Dictionary item, ref Product entity) { // Populate additional state } @@ -621,18 +629,18 @@ public static partial class ProductMapper The generated code **always invokes hooks unconditionally**, regardless of whether they are implemented: ```csharp -// Generated ToItem implementation -public static partial Dictionary ToItem(Product source) +// Generated FromModel implementation +public static partial Dictionary FromModel(Product source) { var item = new Dictionary(capacity: 5); - BeforeToItem(source, item); // Always invoked + BeforeFromModel(source, item); // Always invoked // Property mapping... item["productId"] = new AttributeValue { S = source.ProductId.ToString() }; item["name"] = new AttributeValue { S = source.Name }; - AfterToItem(source, item); // Always invoked + AfterFromModel(source, item); // Always invoked return item; } @@ -650,7 +658,7 @@ Unimplemented hooks **compile away** due to `partial void` semantics: #### 11.4.1 Single-Table PK/SK Composition ```csharp -static partial void AfterToItem(Order order, Dictionary item) +static partial void AfterFromModel(Order order, Dictionary item) { item["pk"] = new AttributeValue { S = $"CUSTOMER#{order.CustomerId}" }; item["sk"] = new AttributeValue { S = $"ORDER#{order.OrderId}" }; @@ -661,12 +669,12 @@ static partial void AfterToItem(Order order, Dictionary #### 11.4.2 Record Type Discrimination ```csharp -static partial void AfterToItem(Customer customer, Dictionary item) +static partial void AfterFromModel(Customer customer, Dictionary item) { item["entityType"] = new AttributeValue { S = nameof(Customer) }; } -static partial void AfterFromItem(Dictionary item, ref Customer entity) +static partial void AfterToModel(Dictionary item, ref Customer entity) { // Validate entity type on read if (item.TryGetValue("entityType", out var typeAttr) && typeAttr.S != nameof(Customer)) @@ -679,7 +687,7 @@ static partial void AfterFromItem(Dictionary item, ref C #### 11.4.3 TTL Attributes ```csharp -static partial void AfterToItem(Session session, Dictionary item) +static partial void AfterFromModel(Session session, Dictionary item) { // Set TTL to 24 hours from now var ttl = DateTimeOffset.UtcNow.AddHours(24).ToUnixTimeSeconds(); @@ -697,7 +705,7 @@ public class Product public Dictionary? AdditionalAttributes { get; set; } } -static partial void AfterToItem(Product source, Dictionary item) +static partial void AfterFromModel(Product source, Dictionary item) { // Merge additional attributes if (source.AdditionalAttributes != null) @@ -709,7 +717,7 @@ static partial void AfterToItem(Product source, Dictionary item, ref Product entity) +static partial void AfterToModel(Dictionary item, ref Product entity) { // Capture unmapped attributes var mappedKeys = new HashSet { "pk", "sk", "productId", "name" }; @@ -722,7 +730,7 @@ static partial void AfterFromItem(Dictionary item, ref P #### 11.4.5 Post-Hydration Normalization ```csharp -static partial void AfterFromItem(Dictionary item, ref User entity) +static partial void AfterToModel(Dictionary item, ref User entity) { // Normalize email to lowercase entity.Email = entity.Email?.ToLowerInvariant(); @@ -736,15 +744,17 @@ static partial void AfterFromItem(Dictionary item, ref U Hooks execute in a deterministic order: -**During ToItem:** -1. `BeforeToItem(source, item)` - item is empty +**During FromModel:** + +1. `BeforeFromModel(source, item)` - item is empty 2. Generated property mapping -3. `AfterToItem(source, item)` - item is fully populated +3. `AfterFromModel(source, item)` - item is fully populated -**During FromItem:** -1. `BeforeFromItem(item)` - item is unmodified +**During ToModel:** + +1. `BeforeToModel(item)` - item is unmodified 2. Generated property mapping and object construction -3. `AfterFromItem(item, ref entity)` - entity is constructed and populated +3. `AfterToModel(item, ref entity)` - entity is constructed and populated ### 11.6 Constraints and Best Practices @@ -814,10 +824,11 @@ Avoid whole-compilation scans beyond necessary discovery. ### 13.3 Supported Signatures (Phase 1) Must recognize: -- `public static partial Dictionary ToItem(T source);` -- `public static partial T FromItem(Dictionary item);` -> Optional: allow `IReadOnlyDictionary` as input for FromItem. +- `public static partial Dictionary FromModel(T source);` +- `public static partial T ToModel(Dictionary item);` + +> Optional: allow `IReadOnlyDictionary` as input for ToModel. ### 13.4 Thread Safety / Determinism - Generator must be deterministic: same input → same output @@ -912,7 +923,7 @@ GitHub Pages is the primary long-form documentation site for DynamoMapper and mu - Naming conventions and defaults - Required vs optional behavior - Converter authoring guide -- Customization hooks (`BeforeToItem`, `AfterFromItem`, etc.) +- Customization hooks (`BeforeFromModel`, `AfterToModel`, etc.) - Diagnostics reference (error codes and meanings) - Single-table DynamoDB patterns and best practices - Migration guide from manual mapping or DynamoDBContext @@ -922,7 +933,8 @@ GitHub Pages should be versioned logically (by major/minor release) but does not ### 16.3 Examples Include at least one “single-table style” example: -- Add pk/sk via `BeforeToItem` + +- Add pk/sk via `BeforeFromModel` - Add `recordType` - Omit optional fields @@ -970,7 +982,7 @@ Phase 2 will introduce an optional fluent DSL for configuration while retaining ```csharp public static partial class JediCharacterMapper { - static partial void BeforeToItem(JediCharacter source, Dictionary item) + static partial void BeforeFromModel(JediCharacter source, Dictionary item) { // Single-table keys + type discriminator 🧠 item["pk"] = new AttributeValue { S = source.Pk }; diff --git a/docs/roadmap/phase-2.md b/docs/roadmap/phase-2.md index 0614d85..0236700 100644 --- a/docs/roadmap/phase-2.md +++ b/docs/roadmap/phase-2.md @@ -199,8 +199,8 @@ map.Property(x => x.Status) [DynamoMapper] public static partial class OrderMapper { - public static partial Dictionary ToItem(Order source); - public static partial Order FromItem(Dictionary item); + public static partial Dictionary FromModel(Order source); + public static partial Order ToModel(Dictionary item); static partial void Configure(DynamoMapBuilder map) { @@ -240,8 +240,8 @@ map.Property(x => x.Status) [DynamoMapper] public static partial class OrderMapper { - public static partial Dictionary ToItem(Order source); - public static partial Order FromItem(Dictionary item); + public static partial Dictionary FromModel(Order source); + public static partial Order ToModel(Dictionary item); static partial void Configure(DynamoMapBuilder map) { @@ -308,9 +308,9 @@ public static partial class ProductMapper { // Attribute specifies one converter [DynamoField(nameof(Product.Category), Converter = typeof(OldCategoryConverter))] - public static partial Dictionary ToItem(Product source); + public static partial Dictionary FromModel(Product source); - public static partial Product FromItem(Dictionary item); + public static partial Product ToModel(Dictionary item); static partial void Configure(DynamoMapBuilder map) { @@ -337,7 +337,7 @@ See also: Hooks defined via DSL are equivalent to Phase 1 partial methods. ```csharp -map.BeforeToItem((source, item) => +map.BeforeFromModel((source, item) => { item["pk"] = new AttributeValue { S = source.Pk }; item["sk"] = new AttributeValue { S = source.Sk }; @@ -465,7 +465,7 @@ public static partial class JediCharacterMapper map.Ignore(x => x.HitRoll); map.Ignore(x => x.DamRoll); - map.BeforeToItem((src, item) => + map.BeforeFromModel((src, item) => { item["pk"] = new AttributeValue { S = src.Pk }; item["sk"] = new AttributeValue { S = src.Sk }; diff --git a/docs/usage/converters.md b/docs/usage/converters.md index 9058c30..91e870d 100644 --- a/docs/usage/converters.md +++ b/docs/usage/converters.md @@ -128,9 +128,9 @@ public class Order public static partial class OrderMapper { [DynamoField(nameof(Order.Status), Converter = typeof(OrderStatusConverter))] - public static partial Dictionary ToItem(Order source); + public static partial Dictionary FromModel(Order source); - public static partial Order FromItem(Dictionary item); + public static partial Order ToModel(Dictionary item); } ``` @@ -142,8 +142,8 @@ In Phase 2, converters can be configured using the fluent DSL: [DynamoMapper] public static partial class OrderMapper { - public static partial Dictionary ToItem(Order source); - public static partial Order FromItem(Dictionary item); + public static partial Dictionary FromModel(Order source); + public static partial Order ToModel(Dictionary item); static partial void Configure(DynamoMapBuilder map) { @@ -183,23 +183,23 @@ public class UserRoleConverter : IDynamoConverter public static partial class UserMapper { [DynamoField(nameof(User.Role), Converter = typeof(UserRoleConverter))] - public static partial Dictionary ToItem(User source); + public static partial Dictionary FromModel(User source); - public static partial Order FromItem(Dictionary item); + public static partial Order ToModel(Dictionary item); } ``` The generator produces code similar to: ```csharp -// Generated ToItem code +// Generated FromModel code if (source.Role != null) { var converter = new UserRoleConverter(); item["role"] = converter.ToAttributeValue(source.Role); } -// Generated FromItem code +// Generated ToModel code UserRole? role = null; if (item.TryGetValue("role", out var roleAttr)) { @@ -403,8 +403,8 @@ public class OrderStatusConverter : IDynamoConverter public static partial class OrderMapper { [DynamoField(nameof(Order.Status), Converter = typeof(OrderStatusConverter))] - public static partial Dictionary ToItem(Order source); - public static partial Order FromItem(Dictionary item); + public static partial Dictionary FromModel(Order source); + public static partial Order ToModel(Dictionary item); } [DynamoMapper(Convention = DynamoNamingConvention.CamelCase)] @@ -412,8 +412,8 @@ public static partial class OrderHistoryMapper { [DynamoField(nameof(OrderHistory.PreviousStatus), Converter = typeof(OrderStatusConverter))] [DynamoField(nameof(OrderHistory.CurrentStatus), Converter = typeof(OrderStatusConverter))] - public static partial Dictionary ToItem(OrderHistory source); - public static partial OrderHistory FromItem(Dictionary item); + public static partial Dictionary FromModel(OrderHistory source); + public static partial OrderHistory ToModel(Dictionary item); } ``` diff --git a/docs/usage/customization-hooks.md b/docs/usage/customization-hooks.md index e4a2bae..9180ed7 100644 --- a/docs/usage/customization-hooks.md +++ b/docs/usage/customization-hooks.md @@ -11,12 +11,12 @@ Customization hooks are first-class extension points in DynamoMapper that allow DynamoMapper provides **four lifecycle hooks** that execute at strategic points during mapping: -| Hook | Phase | When | Use For | -|------|-------|------|---------| -| `BeforeToItem` | ToItem | Before property mapping | Pre-processing, initialization | -| `AfterToItem` | ToItem | After property mapping | PK/SK injection, TTL, metadata | -| `BeforeFromItem` | FromItem | Before property mapping | Validation, preprocessing | -| `AfterFromItem` | FromItem | After object construction | Normalization, computed properties | +| Hook | Phase | When | Use For | +|-------------------|-----------|---------------------------|------------------------------------| +| `BeforeFromModel` | FromModel | Before property mapping | Pre-processing, initialization | +| `AfterFromModel` | FromModel | After property mapping | PK/SK injection, TTL, metadata | +| `BeforeToModel` | ToModel | Before property mapping | Validation, preprocessing | +| `AfterToModel` | ToModel | After object construction | Normalization, computed properties | Hooks are implemented as **optional partial void methods**. If not implemented, they compile away with zero runtime overhead. @@ -34,12 +34,12 @@ DynamoMapper focuses exclusively on mapping properties to/from DynamoDB Attribut All hooks must be declared as `static partial void` on the mapper class. -### BeforeToItem +### BeforeFromModel -Invoked before property mapping during `ToItem`. +Invoked before property mapping during `FromModel`. ```csharp -static partial void BeforeToItem(T source, Dictionary item); +static partial void BeforeFromModel(T source, Dictionary item); ``` **Parameters:** @@ -51,12 +51,12 @@ static partial void BeforeToItem(T source, Dictionary it - Add fixed metadata before mapping - Pre-compute derived values -### AfterToItem +### AfterFromModel -Invoked after property mapping during `ToItem`. +Invoked after property mapping during `FromModel`. ```csharp -static partial void AfterToItem(T source, Dictionary item); +static partial void AfterFromModel(T source, Dictionary item); ``` **Parameters:** @@ -70,12 +70,12 @@ static partial void AfterToItem(T source, Dictionary ite - Merge additional attribute dictionaries - Override generated mappings -### BeforeFromItem +### BeforeToModel -Invoked before property mapping during `FromItem`. +Invoked before property mapping during `ToModel`. ```csharp -static partial void BeforeFromItem(Dictionary item); +static partial void BeforeToModel(Dictionary item); ``` **Parameters:** @@ -87,12 +87,12 @@ static partial void BeforeFromItem(Dictionary item); - Extract unmapped attributes - Log or audit incoming data -### AfterFromItem +### AfterToModel -Invoked after property mapping and object construction during `FromItem`. +Invoked after property mapping and object construction during `ToModel`. ```csharp -static partial void AfterFromItem(Dictionary item, ref T entity); +static partial void AfterToModel(Dictionary item, ref T entity); ``` **Parameters:** @@ -116,10 +116,10 @@ using Amazon.DynamoDBv2.Model; [DynamoMapper(Convention = DynamoNamingConvention.CamelCase)] public static partial class ProductMapper { - public static partial Dictionary ToItem(Product source); - public static partial Product FromItem(Dictionary item); + public static partial Dictionary FromModel(Product source); + public static partial Product ToModel(Dictionary item); - static partial void AfterToItem(Product source, Dictionary item) + static partial void AfterFromModel(Product source, Dictionary item) { item["pk"] = new AttributeValue { S = $"PRODUCT#{source.ProductId}" }; item["sk"] = new AttributeValue { S = "METADATA" }; @@ -137,26 +137,26 @@ For complex mappers, place hooks in a separate file: [DynamoMapper(Convention = DynamoNamingConvention.CamelCase)] public static partial class ProductMapper { - public static partial Dictionary ToItem(Product source); - public static partial Product FromItem(Dictionary item); + public static partial Dictionary FromModel(Product source); + public static partial Product ToModel(Dictionary item); } // ProductMapper.Hooks.cs public static partial class ProductMapper { - static partial void BeforeToItem(Product source, Dictionary item) + static partial void BeforeFromModel(Product source, Dictionary item) { // Pre-processing logic } - static partial void AfterToItem(Product source, Dictionary item) + static partial void AfterFromModel(Product source, Dictionary item) { item["pk"] = new AttributeValue { S = $"PRODUCT#{source.ProductId}" }; item["sk"] = new AttributeValue { S = "METADATA" }; item["recordType"] = new AttributeValue { S = "Product" }; } - static partial void AfterFromItem(Dictionary item, ref Product entity) + static partial void AfterToModel(Dictionary item, ref Product entity) { // Post-hydration logic } @@ -181,10 +181,10 @@ public class Order [DynamoMapper(Convention = DynamoNamingConvention.CamelCase)] public static partial class OrderMapper { - public static partial Dictionary ToItem(Order source); - public static partial Order FromItem(Dictionary item); + public static partial Dictionary FromModel(Order source); + public static partial Order ToModel(Dictionary item); - static partial void AfterToItem(Order source, Dictionary item) + static partial void AfterFromModel(Order source, Dictionary item) { // Composite keys for single-table design item["pk"] = new AttributeValue { S = $"CUSTOMER#{source.CustomerId}" }; @@ -202,15 +202,15 @@ Validate entity types on read: [DynamoMapper(Convention = DynamoNamingConvention.CamelCase)] public static partial class CustomerMapper { - public static partial Dictionary ToItem(Customer source); - public static partial Customer FromItem(Dictionary item); + public static partial Dictionary FromModel(Customer source); + public static partial Customer ToModel(Dictionary item); - static partial void AfterToItem(Customer source, Dictionary item) + static partial void AfterFromModel(Customer source, Dictionary item) { item["entityType"] = new AttributeValue { S = nameof(Customer) }; } - static partial void AfterFromItem(Dictionary item, ref Customer entity) + static partial void AfterToModel(Dictionary item, ref Customer entity) { if (item.TryGetValue("entityType", out var typeAttr)) { @@ -240,10 +240,10 @@ public class Session [DynamoMapper(Convention = DynamoNamingConvention.CamelCase)] public static partial class SessionMapper { - public static partial Dictionary ToItem(Session source); - public static partial Session FromItem(Dictionary item); + public static partial Dictionary FromModel(Session source); + public static partial Session ToModel(Dictionary item); - static partial void AfterToItem(Session source, Dictionary item) + static partial void AfterFromModel(Session source, Dictionary item) { // Set TTL to 24 hours from now var ttl = DateTimeOffset.UtcNow.AddHours(24).ToUnixTimeSeconds(); @@ -270,10 +270,10 @@ public class Product [DynamoMapper(Convention = DynamoNamingConvention.CamelCase)] public static partial class ProductMapper { - public static partial Dictionary ToItem(Product source); - public static partial Product FromItem(Dictionary item); + public static partial Dictionary FromModel(Product source); + public static partial Product ToModel(Dictionary item); - static partial void AfterToItem(Product source, Dictionary item) + static partial void AfterFromModel(Product source, Dictionary item) { // Merge additional attributes if (source.AdditionalAttributes != null) @@ -285,7 +285,7 @@ public static partial class ProductMapper } } - static partial void AfterFromItem(Dictionary item, ref Product entity) + static partial void AfterToModel(Dictionary item, ref Product entity) { // Capture unmapped attributes var mappedKeys = new HashSet @@ -320,12 +320,12 @@ public class User public static partial class UserMapper { [DynamoIgnore(nameof(User.FullName))] - public static partial Dictionary ToItem(User source); + public static partial Dictionary FromModel(User source); [DynamoIgnore(nameof(User.FullName))] - public static partial User FromItem(Dictionary item); + public static partial User ToModel(Dictionary item); - static partial void AfterFromItem(Dictionary item, ref User entity) + static partial void AfterToModel(Dictionary item, ref User entity) { // Normalize email to lowercase entity.Email = entity.Email?.ToLowerInvariant(); @@ -352,10 +352,10 @@ public class Order [DynamoMapper(Convention = DynamoNamingConvention.CamelCase)] public static partial class OrderMapper { - public static partial Dictionary ToItem(Order source); - public static partial Order FromItem(Dictionary item); + public static partial Dictionary FromModel(Order source); + public static partial Order ToModel(Dictionary item); - static partial void AfterToItem(Order source, Dictionary item) + static partial void AfterFromModel(Order source, Dictionary item) { // Main table keys item["pk"] = new AttributeValue { S = $"CUSTOMER#{source.CustomerId}" }; @@ -388,10 +388,10 @@ public class Order [DynamoMapper(Convention = DynamoNamingConvention.CamelCase)] public static partial class OrderMapper { - public static partial Dictionary ToItem(Order source); - public static partial Order FromItem(Dictionary item); + public static partial Dictionary FromModel(Order source); + public static partial Order ToModel(Dictionary item); - static partial void AfterToItem(Order source, Dictionary item) + static partial void AfterFromModel(Order source, Dictionary item) { // Primary access pattern: Get order by customer item["pk"] = new AttributeValue { S = $"CUSTOMER#{source.CustomerId}" }; @@ -419,7 +419,7 @@ public static partial class OrderMapper } } - static partial void BeforeFromItem(Dictionary item) + static partial void BeforeToModel(Dictionary item) { // Validate entity type before mapping if (item.TryGetValue("entityType", out var typeAttr) && typeAttr.S != "Order") @@ -430,7 +430,7 @@ public static partial class OrderMapper } } - static partial void AfterFromItem(Dictionary item, ref Order entity) + static partial void AfterToModel(Dictionary item, ref Order entity) { // Capture metadata var coreKeys = new HashSet @@ -453,10 +453,10 @@ public static partial class OrderMapper [DynamoMapper(Convention = DynamoNamingConvention.CamelCase)] public static partial class ProductMapper { - public static partial Dictionary ToItem(Product source); - public static partial Product FromItem(Dictionary item); + public static partial Dictionary FromModel(Product source); + public static partial Product ToModel(Dictionary item); - static partial void AfterToItem(Product source, Dictionary item) + static partial void AfterFromModel(Product source, Dictionary item) { // Conditional keys based on product type if (source.IsDigital) @@ -483,18 +483,18 @@ public static partial class ProductMapper Hooks execute in a deterministic, predictable order: -### During ToItem: +### During FromModel: 1. Create empty `Dictionary` -2. **BeforeToItem(source, item)** - item is empty +2. **BeforeFromModel(source, item)** - item is empty 3. Map all configured properties to item -4. **AfterToItem(source, item)** - item has all mapped properties +4. **AfterFromModel(source, item)** - item has all mapped properties 5. Return item -### During FromItem: +### During ToModel: 1. Receive `Dictionary` from DynamoDB -2. **BeforeFromItem(item)** - item is unmodified +2. **BeforeToModel(item)** - item is unmodified 3. Map properties and construct entity using object initializer -4. **AfterFromItem(item, ref entity)** - entity is constructed and populated +4. **AfterToModel(item, ref entity)** - entity is constructed and populated 5. Return entity ## Performance Characteristics @@ -504,17 +504,17 @@ Hooks execute in a deterministic, predictable order: Unimplemented hooks compile away completely: ```csharp -// If BeforeToItem is not implemented: -public static partial Dictionary ToItem(Product source) +// If BeforeFromModel is not implemented: +public static partial Dictionary FromModel(Product source) { var item = new Dictionary(5); - // BeforeToItem call is removed by compiler (partial void) + // BeforeFromModel call is removed by compiler (partial void) // Property mapping... item["productId"] = new AttributeValue { S = source.ProductId.ToString() }; - AfterToItem(source, item); // Only this hook is implemented + AfterFromModel(source, item); // Only this hook is implemented return item; } @@ -536,12 +536,12 @@ In Phase 2, hooks can be configured via DSL (though partial methods remain the r [DynamoMapper] public static partial class OrderMapper { - public static partial Dictionary ToItem(Order source); - public static partial Order FromItem(Dictionary item); + public static partial Dictionary FromModel(Order source); + public static partial Order ToModel(Dictionary item); static partial void Configure(DynamoMapBuilder map) { - map.BeforeToItem((source, item) => + map.BeforeFromModel((source, item) => { // Limited DSL hook support item["pk"] = new AttributeValue { S = $"CUSTOMER#{source.CustomerId}" }; @@ -549,7 +549,7 @@ public static partial class OrderMapper } // Partial method hooks are still supported and recommended for complex logic - static partial void AfterToItem(Order source, Dictionary item) + static partial void AfterFromModel(Order source, Dictionary item) { item["sk"] = new AttributeValue { S = $"ORDER#{source.OrderId}" }; item["recordType"] = new AttributeValue { S = "Order" }; @@ -564,14 +564,14 @@ Note: DSL hooks have limited expression support. Partial method hooks are more p 1. **Keep hooks focused and single-purpose** ```csharp // Good: focused on keys - static partial void AfterToItem(Product source, Dictionary item) + static partial void AfterFromModel(Product source, Dictionary item) { item["pk"] = new AttributeValue { S = $"PRODUCT#{source.ProductId}" }; item["sk"] = new AttributeValue { S = "METADATA" }; } // Avoid: mixing concerns - static partial void AfterToItem(Product source, Dictionary item) + static partial void AfterFromModel(Product source, Dictionary item) { item["pk"] = new AttributeValue { S = $"PRODUCT#{source.ProductId}" }; // Don't do business logic in hooks @@ -599,7 +599,7 @@ Note: DSL hooks have limited expression support. Partial method hooks are more p /// PK: CUSTOMER#{CustomerId} /// SK: METADATA /// - static partial void AfterToItem(Customer source, Dictionary item) + static partial void AfterFromModel(Customer source, Dictionary item) { item["pk"] = new AttributeValue { S = $"CUSTOMER#{source.CustomerId}" }; item["sk"] = new AttributeValue { S = "METADATA" }; @@ -608,7 +608,7 @@ Note: DSL hooks have limited expression support. Partial method hooks are more p 5. **Validate entity types early** ```csharp - static partial void BeforeFromItem(Dictionary item) + static partial void BeforeToModel(Dictionary item) { if (!item.TryGetValue("entityType", out var typeAttr) || typeAttr.S != "Product") { @@ -617,7 +617,7 @@ Note: DSL hooks have limited expression support. Partial method hooks are more p } ``` -6. **Use AfterFromItem for computed properties** +6. **Use AfterToModel for computed properties** - Don't add computed properties to DynamoDB - Hydrate them after mapping diff --git a/docs/usage/static-converters.md b/docs/usage/static-converters.md index a9fd0f4..e0cf666 100644 --- a/docs/usage/static-converters.md +++ b/docs/usage/static-converters.md @@ -85,9 +85,9 @@ public class Order public static partial class OrderMapper { [DynamoField(nameof(Order.Status), ToMethod = nameof(ToOrderStatus), FromMethod = nameof(FromOrderStatus))] - public static partial Dictionary ToItem(Order source); + public static partial Dictionary FromModel(Order source); - public static partial Order FromItem(Dictionary item); + public static partial Order ToModel(Dictionary item); // Static conversion methods static AttributeValue ToOrderStatus(OrderStatus status) @@ -112,7 +112,7 @@ Configure static methods using the `[DynamoField]` attribute: [DynamoField(nameof(Order.Status), ToMethod = nameof(ToOrderStatus), FromMethod = nameof(FromOrderStatus))] -public static partial Dictionary ToItem(Order source); +public static partial Dictionary FromModel(Order source); ``` **Properties:** @@ -132,8 +132,8 @@ In Phase 2, static methods can be configured using the fluent DSL: [DynamoMapper] public static partial class OrderMapper { - public static partial Dictionary ToItem(Order source); - public static partial Order FromItem(Dictionary item); + public static partial Dictionary FromModel(Order source); + public static partial Order ToModel(Dictionary item); static partial void Configure(DynamoMapBuilder map) { @@ -163,9 +163,9 @@ public static partial class ProductMapper { [DynamoField(nameof(Product.Category), ToMethod = nameof(ToCategory), FromMethod = nameof(FromCategory))] [DynamoField(nameof(Product.Status), ToMethod = nameof(ToProductStatus), FromMethod = nameof(FromProductStatus))] - public static partial Dictionary ToItem(Product source); + public static partial Dictionary FromModel(Product source); - public static partial Product FromItem(Dictionary item); + public static partial Product ToModel(Dictionary item); // Category conversion static AttributeValue ToCategory(ProductCategory category) @@ -206,9 +206,9 @@ public class User public static partial class UserMapper { [DynamoField(nameof(User.Role), ToMethod = nameof(ToUserRole), FromMethod = nameof(FromUserRole))] - public static partial Dictionary ToItem(User source); + public static partial Dictionary FromModel(User source); - public static partial User FromItem(Dictionary item); + public static partial User ToModel(Dictionary item); // Methods work with non-nullable UserRole static AttributeValue ToUserRole(UserRole role) @@ -254,9 +254,9 @@ public class Money public static partial class ProductMapper { [DynamoField(nameof(Product.Price), ToMethod = nameof(ToMoney), FromMethod = nameof(FromMoney))] - public static partial Dictionary ToItem(Product source); + public static partial Dictionary FromModel(Product source); - public static partial Product FromItem(Dictionary item); + public static partial Product ToModel(Dictionary item); static AttributeValue ToMoney(Money money) { diff --git a/examples/DynamoMapper.FieldLevelOverride/Program.cs b/examples/DynamoMapper.FieldLevelOverride/Program.cs index d7188a4..3b78f37 100644 --- a/examples/DynamoMapper.FieldLevelOverride/Program.cs +++ b/examples/DynamoMapper.FieldLevelOverride/Program.cs @@ -12,7 +12,7 @@ ["ANOTHER_NAME"] = new() { S = "Optional text" }, }; -var myEntity = ExampleDtoMapper.FromItem(exampleAttributes); +var myEntity = ExampleDtoMapper.ToModel(exampleAttributes); [DynamoMapper] [DynamoField( @@ -31,9 +31,9 @@ )] internal static partial class ExampleDtoMapper { - internal static partial Dictionary ToItem(ExampleDto source); + internal static partial Dictionary FromModel(ExampleDto source); - internal static partial ExampleDto FromItem(Dictionary item); + internal static partial ExampleDto ToModel(Dictionary item); internal static AttributeValue CustomValueToAttribute(ExampleDto source) => // custom logic here before returning an attribute value diff --git a/examples/DynamoMapper.SimpleExample/Program.cs b/examples/DynamoMapper.SimpleExample/Program.cs index 4946b75..29df78f 100644 --- a/examples/DynamoMapper.SimpleExample/Program.cs +++ b/examples/DynamoMapper.SimpleExample/Program.cs @@ -79,7 +79,7 @@ ["nullable_enum"] = new() { S = "Shipped" }, }; -var myEntity = ExampleModelMapper.FromItem(exampleAttributes); +var myEntity = ExampleModelMapper.ToModel(exampleAttributes); [DynamoMapper( Convention = DynamoNamingConvention.SnakeCase, @@ -91,9 +91,9 @@ )] internal static partial class ExampleModelMapper { - private static partial Dictionary ToItem(ExampleModel source); + private static partial Dictionary FromModel(ExampleModel source); - internal static partial ExampleModel FromItem(Dictionary item); + internal static partial ExampleModel ToModel(Dictionary item); } internal class ExampleModel diff --git a/src/LayeredCraft.DynamoMapper.Generators/Emitters/MapperEmitter.cs b/src/LayeredCraft.DynamoMapper.Generators/Emitters/MapperEmitter.cs index a75eebc..bee5498 100644 --- a/src/LayeredCraft.DynamoMapper.Generators/Emitters/MapperEmitter.cs +++ b/src/LayeredCraft.DynamoMapper.Generators/Emitters/MapperEmitter.cs @@ -26,14 +26,14 @@ private static string GeneratedCodeAttribute internal static void Generate(SourceProductionContext context, MapperInfo mapperInfo) { - var toAssignments = mapperInfo - .ModelClass!.Properties.Where(p => !string.IsNullOrEmpty(p.ToAssignments)) - .Select(p => p.ToAssignments) + var fromModelAssignments = mapperInfo + .ModelClass!.Properties.Where(p => !string.IsNullOrEmpty(p.FromModelAssignments)) + .Select(p => p.FromModelAssignments) .ToArray(); - var fromAssignments = mapperInfo - .ModelClass!.Properties.Where(p => !string.IsNullOrEmpty(p.FromAssignment)) - .Select(p => p.FromAssignment) + var toModelAssignments = mapperInfo + .ModelClass!.Properties.Where(p => !string.IsNullOrEmpty(p.ToModelAssignment)) + .Select(p => p.ToModelAssignment) .ToArray(); var model = new @@ -41,11 +41,11 @@ internal static void Generate(SourceProductionContext context, MapperInfo mapper GeneratedCodeAttribute, mapperInfo.MapperClass, ModelClass = mapperInfo.ModelClass!, - ToAssignments = toAssignments, - FromAssignments = fromAssignments, + FromModelAssignments = fromModelAssignments, + ToModelAssignments = toModelAssignments, MapperClassNamespace = mapperInfo.MapperClass?.Namespace, MapperClassSignature = mapperInfo.MapperClass?.ClassSignature, - DictionaryCapacity = toAssignments.Length, + DictionaryCapacity = fromModelAssignments.Length, }; var outputCode = TemplateHelper.Render("Templates.Mapper.scriban", model); diff --git a/src/LayeredCraft.DynamoMapper.Generators/GeneratorContext.cs b/src/LayeredCraft.DynamoMapper.Generators/GeneratorContext.cs index d9404fb..36a6641 100644 --- a/src/LayeredCraft.DynamoMapper.Generators/GeneratorContext.cs +++ b/src/LayeredCraft.DynamoMapper.Generators/GeneratorContext.cs @@ -33,16 +33,16 @@ CancellationToken cancellationToken internal Dictionary IgnoreOptions { get; } /// - /// Indicates whether a ToItem method is defined in the mapper class. Used to determine if - /// properties need to be validated for serialization. + /// Indicates whether a FromModel method is defined in the mapper class (Model → DynamoDB). + /// Used to determine if properties need to be validated for serialization. /// - internal bool HasToItemMethod { get; set; } + internal bool HasFromModelMethod { get; set; } /// - /// Indicates whether a FromItem method is defined in the mapper class. Used to determine if - /// properties need to be validated for deserialization. + /// Indicates whether a ToModel method is defined in the mapper class (DynamoDB → Model). + /// Used to determine if properties need to be validated for deserialization. /// - internal bool HasFromItemMethod { get; set; } + internal bool HasToModelMethod { get; set; } } internal static class GeneratorContextExtensions diff --git a/src/LayeredCraft.DynamoMapper.Generators/Models/MapperClassInfo.cs b/src/LayeredCraft.DynamoMapper.Generators/Models/MapperClassInfo.cs index 03e6622..0ffa47f 100644 --- a/src/LayeredCraft.DynamoMapper.Generators/Models/MapperClassInfo.cs +++ b/src/LayeredCraft.DynamoMapper.Generators/Models/MapperClassInfo.cs @@ -8,8 +8,8 @@ internal sealed record MapperClassInfo( string Name, string Namespace, string ClassSignature, - string? ToItemSignature, - string? FromItemSignature, + string? FromModelSignature, + string? ToModelSignature, LocationInfo? Location ); @@ -29,10 +29,11 @@ GeneratorContext context /* * to determine what mappers to generate, we need to look for methods using these rules: - * - Methods starting with `To` (e.g., ToItem, ToModel) -> map from POCO to - * AttributeValue - * - Methods starting with `From` (e.g., FromItem, FromModel) -> map from AttributeValue - * to POCO + * - Methods starting with `From` (e.g., FromModel, FromProduct) -> map from POCO/Model + * to + * AttributeValue (Model → DynamoDB) + * - Methods starting with `To` (e.g., ToModel, ToProduct) -> map from AttributeValue + * to POCO/Model (DynamoDB → Model) * * Rules: * - at least one is needed, but both are not required @@ -41,27 +42,27 @@ GeneratorContext context var methods = classSymbol.GetMembers().OfType().ToArray(); - var toItemMethod = methods.FirstOrDefault(m => IsToMethod(m, context)); - var fromItemMethod = methods.FirstOrDefault(m => IsFromMethod(m, context)); + var fromModelMethod = methods.FirstOrDefault(m => IsFromMethod(m, context)); + var toModelMethod = methods.FirstOrDefault(m => IsToMethod(m, context)); // If there's an error in POCO type matching, propagate it - return EnsurePocoTypesMatch(toItemMethod, fromItemMethod, classSymbol) + return EnsurePocoTypesMatch(fromModelMethod, toModelMethod, classSymbol) .Bind(modelType => { var classSignature = GetClassSignature(classSymbol); - var toItemSignature = toItemMethod?.Map(GetMethodSignature); - var fromItemSignature = fromItemMethod?.Map(GetMethodSignature); + var fromModelSignature = fromModelMethod?.Map(GetMethodSignature); + var toModelSignature = toModelMethod?.Map(GetMethodSignature); var namespaceStatement = classSymbol.ContainingNamespace is { IsGlobalNamespace: false } ns ? $"namespace {ns.ToDisplayString()};" : string.Empty; - toItemMethod + fromModelMethod ?.Parameters.FirstOrDefault() - ?.Name.Tap(name => context.MapperOptions.ToMethodParameterName = name); - fromItemMethod + ?.Name.Tap(name => context.MapperOptions.FromModelParameterName = name); + toModelMethod ?.Parameters.FirstOrDefault() - ?.Name.Tap(name => context.MapperOptions.FromMethodParameterName = name); + ?.Name.Tap(name => context.MapperOptions.ToModelParameterName = name); return DiagnosticResult<(MapperClassInfo, ITypeSymbol)>.Success( ( @@ -69,8 +70,8 @@ GeneratorContext context classSymbol.Name, namespaceStatement, classSignature, - toItemSignature, - fromItemSignature, + fromModelSignature, + toModelSignature, context.TargetNode.CreateLocationInfo() ), modelType @@ -81,63 +82,65 @@ GeneratorContext context } private static DiagnosticResult EnsurePocoTypesMatch( - IMethodSymbol? toItemMethod, - IMethodSymbol? fromItemMethod, + IMethodSymbol? fromModelMethod, + IMethodSymbol? toModelMethod, INamedTypeSymbol mapperClassSymbol ) { - if (toItemMethod is null && fromItemMethod is null) + if (fromModelMethod is null && toModelMethod is null) return DiagnosticResult.Failure( DiagnosticDescriptors.NoMapperMethodsFound, mapperClassSymbol.CreateLocationInfo(), mapperClassSymbol.Name ); - var toItemPocoType = toItemMethod?.Parameters[0].Type; - var fromItemPocoType = fromItemMethod?.ReturnType; + var fromModelPocoType = fromModelMethod?.Parameters[0].Type; + var toModelPocoType = toModelMethod?.ReturnType; if ( - toItemPocoType is not null - && fromItemPocoType is not null - && !SymbolEqualityComparer.Default.Equals(toItemPocoType, fromItemPocoType) + fromModelPocoType is not null + && toModelPocoType is not null + && !SymbolEqualityComparer.Default.Equals(fromModelPocoType, toModelPocoType) ) return DiagnosticResult.Failure( DiagnosticDescriptors.MismatchedPocoTypes, - toItemMethod?.CreateLocationInfo(), - toItemMethod?.Name, - fromItemMethod?.Name, - toItemPocoType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), - fromItemPocoType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + fromModelMethod?.CreateLocationInfo(), + fromModelMethod?.Name, + toModelMethod?.Name, + fromModelPocoType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), + toModelPocoType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) ); - return DiagnosticResult.Success(toItemPocoType ?? fromItemPocoType!); + return DiagnosticResult.Success(fromModelPocoType ?? toModelPocoType!); } /// - /// To method must be: + /// To method (ToModel, ToProduct, etc.) maps DynamoDB to Model (DynamoDB → Model). + /// Must be: /// /// partial not implemented - /// one parameter return an Attribute value dictionary + /// one parameter parameter is an Attribute value dictionary /// /// private static bool IsToMethod(IMethodSymbol method, GeneratorContext context) => method.Name.StartsWith(ToMethodPrefix) && method is { IsPartialDefinition: true, PartialImplementationPart: null, Parameters.Length: 1 } - && IsAttributeValueDictionary(method.ReturnType, context); + && IsAttributeValueDictionary(method.Parameters[0].Type, context); /// - /// From method must be: + /// From method (FromModel, FromProduct, etc.) maps Model to DynamoDB (Model → DynamoDB). + /// Must be: /// /// partial not implemented - /// one parameter parameter is an Attribute value dictionary + /// one parameter return an Attribute value dictionary /// /// private static bool IsFromMethod(IMethodSymbol method, GeneratorContext context) => method.Name.StartsWith(FromMethodPrefix) && method is { IsPartialDefinition: true, PartialImplementationPart: null, Parameters.Length: 1 } - && IsAttributeValueDictionary(method.Parameters[0].Type, context); + && IsAttributeValueDictionary(method.ReturnType, context); private static bool IsAttributeValueDictionary(ITypeSymbol type, GeneratorContext context) => type is INamedTypeSymbol { IsGenericType: true } namedType diff --git a/src/LayeredCraft.DynamoMapper.Generators/Models/MapperInfo.cs b/src/LayeredCraft.DynamoMapper.Generators/Models/MapperInfo.cs index d32eb7d..54a3491 100644 --- a/src/LayeredCraft.DynamoMapper.Generators/Models/MapperInfo.cs +++ b/src/LayeredCraft.DynamoMapper.Generators/Models/MapperInfo.cs @@ -28,8 +28,8 @@ internal static MapperInfo Create(INamedTypeSymbol classSymbol, GeneratorContext var (mapperClassInfo, modelTypeSymbol) = mapperResult.Value; // Set method context flags so property validation knows which methods exist - context.HasToItemMethod = mapperClassInfo.ToItemSignature != null; - context.HasFromItemMethod = mapperClassInfo.FromItemSignature != null; + context.HasFromModelMethod = mapperClassInfo.FromModelSignature != null; + context.HasToModelMethod = mapperClassInfo.ToModelSignature != null; var (modelClassInfo, diagnosticInfos) = ModelClassInfo.Create(modelTypeSymbol, context); diff --git a/src/LayeredCraft.DynamoMapper.Generators/Models/PropertyInfo.cs b/src/LayeredCraft.DynamoMapper.Generators/Models/PropertyInfo.cs index 3339e37..ec55f56 100644 --- a/src/LayeredCraft.DynamoMapper.Generators/Models/PropertyInfo.cs +++ b/src/LayeredCraft.DynamoMapper.Generators/Models/PropertyInfo.cs @@ -4,7 +4,7 @@ namespace DynamoMapper.Generator.Models; -internal sealed record PropertyInfo(string? FromAssignment, string? ToAssignments); +internal sealed record PropertyInfo(string? ToModelAssignment, string? FromModelAssignments); internal static class PropertyInfoExtensions { diff --git a/src/LayeredCraft.DynamoMapper.Generators/Options/MapperOptions.cs b/src/LayeredCraft.DynamoMapper.Generators/Options/MapperOptions.cs index ee6e766..0ebbeed 100644 --- a/src/LayeredCraft.DynamoMapper.Generators/Options/MapperOptions.cs +++ b/src/LayeredCraft.DynamoMapper.Generators/Options/MapperOptions.cs @@ -12,8 +12,8 @@ internal class MapperOptions internal bool OmitEmptyStrings { get; set; } = false; internal bool OmitNullStrings { get; set; } = true; - internal string ToMethodParameterName { get; set; } = "source"; - internal string FromMethodParameterName { get; set; } = "item"; + internal string FromModelParameterName { get; set; } = "source"; + internal string ToModelParameterName { get; set; } = "item"; internal Func KeyNamingConventionConverter { diff --git a/src/LayeredCraft.DynamoMapper.Generators/PropertyMapping/Models/PropertyMappingSpec.cs b/src/LayeredCraft.DynamoMapper.Generators/PropertyMapping/Models/PropertyMappingSpec.cs index c2ad70d..fe21faf 100644 --- a/src/LayeredCraft.DynamoMapper.Generators/PropertyMapping/Models/PropertyMappingSpec.cs +++ b/src/LayeredCraft.DynamoMapper.Generators/PropertyMapping/Models/PropertyMappingSpec.cs @@ -6,11 +6,11 @@ namespace DynamoMapper.Generator.PropertyMapping.Models; /// /// The property name. /// The type mapping strategy. -/// Method specification for serialization (ToItem), or null if mapper doesn't have ToItem. -/// Method specification for deserialization (FromItem), or null if mapper doesn't have FromItem. +/// Method specification for serialization (FromModel - Model → DynamoDB), or null if mapper doesn't have FromModel. +/// Method specification for deserialization (ToModel - DynamoDB → Model), or null if mapper doesn't have ToModel. internal sealed record PropertyMappingSpec( string PropertyName, TypeMappingStrategy? TypeStrategy, - MethodCallSpec? ToItemMethod, - MethodCallSpec? FromItemMethod + MethodCallSpec? FromModelMethod, + MethodCallSpec? ToModelMethod ); diff --git a/src/LayeredCraft.DynamoMapper.Generators/PropertyMapping/PropertyMappingCodeRenderer.cs b/src/LayeredCraft.DynamoMapper.Generators/PropertyMapping/PropertyMappingCodeRenderer.cs index bc2873f..e0f0860 100644 --- a/src/LayeredCraft.DynamoMapper.Generators/PropertyMapping/PropertyMappingCodeRenderer.cs +++ b/src/LayeredCraft.DynamoMapper.Generators/PropertyMapping/PropertyMappingCodeRenderer.cs @@ -21,61 +21,65 @@ internal static PropertyInfo Render( GeneratorContext context ) { - // FromItem requires both: setter on property AND FromItem method exists - var fromAssignment = - context.HasFromItemMethod && analysis.HasSetter && spec.FromItemMethod is not null - ? RenderFromAssignment(spec, context) + // ToModel (DynamoDB → Model) requires both: setter on property AND ToModel method exists + var toModelAssignment = + context.HasToModelMethod && analysis.HasSetter && spec.ToModelMethod is not null + ? RenderToModelAssignment(spec, context) : null; - // ToItem requires both: getter on property AND ToItem method exists - var toAssignments = - context.HasToItemMethod && analysis.HasGetter && spec.ToItemMethod is not null - ? RenderToAssignment(spec) + // FromModel (Model → DynamoDB) requires both: getter on property AND FromModel method + // exists + var fromModelAssignments = + context.HasFromModelMethod && analysis.HasGetter && spec.FromModelMethod is not null + ? RenderFromModelAssignment(spec) : null; - return new PropertyInfo(fromAssignment, toAssignments); + return new PropertyInfo(toModelAssignment, fromModelAssignments); } /// - /// Renders the FromAssignment string for deserialization. Format: PropertyName = + /// Renders the ToModel assignment string for deserialization (DynamoDB → Model). Format: PropertyName = /// paramName.MethodName<Generic>(args), OR PropertyName = CustomMethodName(args), for /// custom methods /// - private static string RenderFromAssignment(PropertyMappingSpec spec, GeneratorContext context) + private static string RenderToModelAssignment( + PropertyMappingSpec spec, + GeneratorContext context + ) { - Debug.Assert(spec.FromItemMethod is not null, "FromItemMethod should not be null"); + Debug.Assert(spec.ToModelMethod is not null, "ToModelMethod should not be null"); Debug.Assert( - spec.FromItemMethod!.IsCustomMethod || spec.TypeStrategy is not null, + spec.ToModelMethod!.IsCustomMethod || spec.TypeStrategy is not null, "TypeStrategy should not be null for standard methods" ); - var args = string.Join(", ", spec.FromItemMethod.Arguments.Select(a => a.Value)); + var args = string.Join(", ", spec.ToModelMethod.Arguments.Select(a => a.Value)); - var methodCall = spec.FromItemMethod.IsCustomMethod - ? $"{spec.FromItemMethod.MethodName}({args})" // Custom: MethodName(item) - : $"{context.MapperOptions.FromMethodParameterName}.{spec.FromItemMethod.MethodName}{spec.TypeStrategy!.GenericArgument}({args})"; // Standard: item.GetXxx(args) + var methodCall = spec.ToModelMethod.IsCustomMethod + ? $"{spec.ToModelMethod.MethodName}({args})" // Custom: MethodName(item) + : $"{context.MapperOptions.ToModelParameterName}.{spec.ToModelMethod.MethodName}{spec.TypeStrategy!.GenericArgument}({args})"; // Standard: item.GetXxx(args) return $"{spec.PropertyName} = {methodCall},"; } /// - /// Renders the ToAssignment string for serialization. Format: .MethodName<Generic>(args) - /// Custom ToMethods are rendered as .Set("key", CustomMethod(source)) + /// Renders the FromModel assignment string for serialization (Model → DynamoDB). Format: .MethodName<Generic>(args) + /// Custom FromModel methods are rendered as .Set("key", CustomMethod(source)) /// - private static string RenderToAssignment(PropertyMappingSpec spec) + private static string RenderFromModelAssignment(PropertyMappingSpec spec) { - Debug.Assert(spec.ToItemMethod is not null, "ToItemMethod should not be null"); + Debug.Assert(spec.FromModelMethod is not null, "FromModelMethod should not be null"); Debug.Assert( - spec.ToItemMethod!.IsCustomMethod || spec.TypeStrategy is not null, + spec.FromModelMethod!.IsCustomMethod || spec.TypeStrategy is not null, "TypeStrategy should not be null for standard methods" ); - var args = string.Join(", ", spec.ToItemMethod.Arguments.Select(a => a.Value)); + var args = string.Join(", ", spec.FromModelMethod.Arguments.Select(a => a.Value)); - var methodCall = spec.ToItemMethod.IsCustomMethod - ? $".{spec.ToItemMethod.MethodName}({args})" // Custom: .Set("key", + var methodCall = spec.FromModelMethod.IsCustomMethod + ? $".{spec.FromModelMethod.MethodName}({args})" // Custom: .Set("key", // CustomMethod(source)) - : $".{spec.ToItemMethod.MethodName}{spec.TypeStrategy!.GenericArgument}({args})"; // Standard: .SetXxx(args) + : $".{spec.FromModelMethod.MethodName}{spec.TypeStrategy!.GenericArgument}({args})"; // Standard: .SetXxx(args) return methodCall; } diff --git a/src/LayeredCraft.DynamoMapper.Generators/PropertyMapping/PropertyMappingSpecBuilder.cs b/src/LayeredCraft.DynamoMapper.Generators/PropertyMapping/PropertyMappingSpecBuilder.cs index 5ec65fd..68f38dc 100644 --- a/src/LayeredCraft.DynamoMapper.Generators/PropertyMapping/PropertyMappingSpecBuilder.cs +++ b/src/LayeredCraft.DynamoMapper.Generators/PropertyMapping/PropertyMappingSpecBuilder.cs @@ -34,28 +34,28 @@ GeneratorContext context var ignoreOptions = context.IgnoreOptions.TryGetValue(analysis.PropertyName, out var opts) ? opts : null; - var shouldIgnoreToItem = + var shouldIgnoreFromModel = ignoreOptions?.Ignore is IgnoreMapping.All or IgnoreMapping.FromModel; - var shouldIgnoreFromItem = + var shouldIgnoreToModel = ignoreOptions?.Ignore is IgnoreMapping.All or IgnoreMapping.ToModel; // Only build methods if the mapper has them defined - var fromItemMethod = context.HasFromItemMethod - ? shouldIgnoreFromItem + var toModelMethod = context.HasToModelMethod + ? shouldIgnoreToModel ? null - : BuildFromItemMethod(analysis, strategy, key, context) + : BuildToModelMethod(analysis, strategy, key, context) : null; - var toItemMethod = context.HasToItemMethod - ? shouldIgnoreToItem + var fromModelMethod = context.HasFromModelMethod + ? shouldIgnoreFromModel ? null - : BuildToItemMethod(analysis, strategy, key, context) + : BuildFromModelMethod(analysis, strategy, key, context) : null; return new PropertyMappingSpec( analysis.PropertyName, strategy, - toItemMethod, - fromItemMethod + fromModelMethod, + toModelMethod ); } @@ -68,21 +68,18 @@ private static string GetAttributeKey(PropertyAnalysis analysis, GeneratorContex ?? context.MapperOptions.KeyNamingConventionConverter(analysis.PropertyName); /// - /// Builds the method specification for deserialization (FromItem). Method name format: + /// Builds the method specification for deserialization (ToModel - DynamoDB → Model). Method name format: /// Get{Nullable}{TypeName}{Generic} Arguments: [key, ...type-specific args, requiredness, /// (optional) kind] /// - private static MethodCallSpec BuildFromItemMethod( + private static MethodCallSpec BuildToModelMethod( PropertyAnalysis analysis, [NotNull] TypeMappingStrategy? strategy, string key, GeneratorContext context ) { - Debug.Assert( - strategy is not null, - "TypeMappingStrategy cannot be null for FromItem method" - ); + Debug.Assert(strategy is not null, "TypeMappingStrategy cannot be null for ToModel method"); var methodName = $"Get{strategy!.NullableModifier}{strategy.TypeName}"; @@ -130,21 +127,24 @@ GeneratorContext context } /// - /// Builds the method specification for serialization (ToItem). Method name format: + /// Builds the method specification for serialization (FromModel - Model → DynamoDB). Method name format: /// Set{TypeName}{Generic} (no Nullable modifier) Arguments: [key, sourceProperty, ...type-specific /// args, omitEmptyStrings, omitNullStrings, (optional) kind] /// - private static MethodCallSpec BuildToItemMethod( + private static MethodCallSpec BuildFromModelMethod( PropertyAnalysis analysis, [NotNull] TypeMappingStrategy? strategy, string key, GeneratorContext context ) { - Debug.Assert(strategy is not null, "TypeMappingStrategy cannot be null for ToItem method"); + Debug.Assert( + strategy is not null, + "TypeMappingStrategy cannot be null for FromModel method" + ); var methodName = $"Set{strategy!.TypeName}"; - var paramName = context.MapperOptions.ToMethodParameterName; + var paramName = context.MapperOptions.FromModelParameterName; var args = new List { @@ -210,33 +210,33 @@ GeneratorContext context { var key = GetAttributeKey(analysis, context); - // Only build FromItem method if mapper has FromItem defined - var fromItemMethod = context.HasFromItemMethod + // Only build ToModel method if mapper has ToModel defined (DynamoDB → Model) + var toModelMethod = context.HasToModelMethod ? fieldOptions.FromMethod is not null - ? BuildCustomFromItemMethod(fieldOptions.FromMethod, context) - : BuildFromItemMethod(analysis, strategy, key, context) + ? BuildCustomToModelMethod(fieldOptions.FromMethod, context) + : BuildToModelMethod(analysis, strategy, key, context) : null; - // Only build ToItem method if mapper has ToItem defined - var toItemMethod = context.HasToItemMethod + // Only build FromModel method if mapper has FromModel defined (Model → DynamoDB) + var fromModelMethod = context.HasFromModelMethod ? fieldOptions.ToMethod is not null - ? BuildCustomToItemMethod(fieldOptions.ToMethod, analysis, context) - : BuildToItemMethod(analysis, strategy, key, context) + ? BuildCustomFromModelMethod(fieldOptions.ToMethod, analysis, context) + : BuildFromModelMethod(analysis, strategy, key, context) : null; return new PropertyMappingSpec( analysis.PropertyName, strategy, - toItemMethod, - fromItemMethod + fromModelMethod, + toModelMethod ); } /// - /// Builds a custom FromItem method call. Custom FromItem methods receive the entire item + /// Builds a custom ToModel method call (DynamoDB → Model). Custom ToModel methods receive the entire item /// dictionary as their only argument. /// - private static MethodCallSpec BuildCustomFromItemMethod( + private static MethodCallSpec BuildCustomToModelMethod( string methodName, GeneratorContext context ) @@ -244,7 +244,7 @@ GeneratorContext context var args = new[] { new ArgumentSpec( - context.MapperOptions.FromMethodParameterName, + context.MapperOptions.ToModelParameterName, ArgumentSource.FieldOverride ), }; @@ -252,17 +252,17 @@ GeneratorContext context } /// - /// Builds a custom ToItem method call. Custom ToItem methods receive the entire source object + /// Builds a custom FromModel method call (Model → DynamoDB). Custom FromModel methods receive the entire source object /// and return an AttributeValue to be used within a .Set() call. /// - private static MethodCallSpec BuildCustomToItemMethod( + private static MethodCallSpec BuildCustomFromModelMethod( string methodName, PropertyAnalysis analysis, GeneratorContext context ) { var key = GetAttributeKey(analysis, context); - var paramName = context.MapperOptions.ToMethodParameterName; + var paramName = context.MapperOptions.FromModelParameterName; var args = new[] { diff --git a/src/LayeredCraft.DynamoMapper.Generators/PropertyMapping/TypeMappingStrategyResolver.cs b/src/LayeredCraft.DynamoMapper.Generators/PropertyMapping/TypeMappingStrategyResolver.cs index dcbd09e..f5f0f06 100644 --- a/src/LayeredCraft.DynamoMapper.Generators/PropertyMapping/TypeMappingStrategyResolver.cs +++ b/src/LayeredCraft.DynamoMapper.Generators/PropertyMapping/TypeMappingStrategyResolver.cs @@ -33,31 +33,31 @@ GeneratorContext context // Skip validation if property won't be used in any generated methods // Also skip if a custom method is provided for that direction (no auto-mapping needed) - var willBeUsedInToItem = - context.HasToItemMethod + var willBeUsedInFromModel = + context.HasFromModelMethod && analysis.HasGetter && analysis.FieldOptions?.ToMethod == null; - var willBeUsedInFromItem = - context.HasFromItemMethod + var willBeUsedInToModel = + context.HasToModelMethod && analysis.HasSetter && analysis.FieldOptions?.FromMethod == null; - if (!willBeUsedInToItem && !willBeUsedInFromItem) + if (!willBeUsedInFromModel && !willBeUsedInToModel) return DiagnosticResult.Success(null); // Check if property should be ignored based on DynamoIgnoreOptions if (context.IgnoreOptions.TryGetValue(analysis.PropertyName, out var ignoreOptions)) { - var shouldIgnoreToItem = + var shouldIgnoreFromModel = ignoreOptions.Ignore is IgnoreMapping.All or IgnoreMapping.FromModel; - var shouldIgnoreFromItem = + var shouldIgnoreToModel = ignoreOptions.Ignore is IgnoreMapping.All or IgnoreMapping.ToModel; // If property should be ignored in all relevant directions, skip mapping if ( - (!willBeUsedInToItem || shouldIgnoreToItem) - && (!willBeUsedInFromItem || shouldIgnoreFromItem) + (!willBeUsedInFromModel || shouldIgnoreFromModel) + && (!willBeUsedInToModel || shouldIgnoreToModel) ) return DiagnosticResult.Success(null); } @@ -199,7 +199,7 @@ GeneratorContext context // Nullable enums don't need a default value (they can be null) string[] fromArgs = analysis.Nullability.IsNullableType ? [enumFormat] - : [$"{enumName}.{enumType.MemberNames.First()}", enumFormat]; +mType.MemberNames.First()}", enumFormat]; string[] toArgs = [enumFormat]; diff --git a/src/LayeredCraft.DynamoMapper.Generators/Templates/Mapper.scriban b/src/LayeredCraft.DynamoMapper.Generators/Templates/Mapper.scriban index 5e04cc5..71728e1 100644 --- a/src/LayeredCraft.DynamoMapper.Generators/Templates/Mapper.scriban +++ b/src/LayeredCraft.DynamoMapper.Generators/Templates/Mapper.scriban @@ -19,23 +19,23 @@ using Amazon.DynamoDBv2.Model; {{~ end ~}} {{ mapper_class_signature }} { - {{~ if mapper_class.to_item_signature != null ~}} + {{~ if mapper_class.from_model_signature != null ~}} {{ generated_code_attribute }} - {{ mapper_class.to_item_signature }} => + {{ mapper_class.from_model_signature }} => new Dictionary({{ dictionary_capacity }}){{ if dictionary_capacity == 0 }};{{ end }} - {{~ for assignment in to_assignments ~}} + {{~ for assignment in from_model_assignments ~}} {{ assignment }}{{ if for.last }};{{ end }} {{~ end ~}} {{~ end ~}} - {{~ if mapper_class.to_item_signature != null && mapper_class.from_item_signature != null ~}} + {{~ if mapper_class.from_model_signature != null && mapper_class.to_model_signature != null ~}} {{~ end ~}} - {{~ if mapper_class.from_item_signature != null ~}} + {{~ if mapper_class.to_model_signature != null ~}} {{ generated_code_attribute }} - {{ mapper_class.from_item_signature }} => + {{ mapper_class.to_model_signature }} => new {{ model_class.fully_qualified_type }} { - {{~ for assignment in from_assignments ~}} + {{~ for assignment in to_model_assignments ~}} {{ assignment }} {{~ end ~}} }; diff --git a/src/LayeredCraft.DynamoMapper.Runtime/IgnoreMapping.cs b/src/LayeredCraft.DynamoMapper.Runtime/IgnoreMapping.cs index 6ecc742..7ed2d44 100644 --- a/src/LayeredCraft.DynamoMapper.Runtime/IgnoreMapping.cs +++ b/src/LayeredCraft.DynamoMapper.Runtime/IgnoreMapping.cs @@ -7,22 +7,22 @@ namespace DynamoMapper.Runtime; public enum IgnoreMapping { /// - /// Skip this property in both ToItem (model to DynamoDB) and FromItem + /// Skip this property in both FromModel (model to DynamoDB) and ToModel /// (DynamoDB to model) mappings. /// All, /// - /// Skip this property when mapping from DynamoDB to the .NET model (FromItem + /// Skip this property when mapping from DynamoDB to the .NET model (ToModel /// method). The property will still be mapped when serializing to DynamoDB - /// (ToItem method). + /// (FromModel method). /// ToModel, /// - /// Skip this property when mapping from the .NET model to DynamoDB (ToItem + /// Skip this property when mapping from the .NET model to DynamoDB (FromModel /// method). The property will still be mapped when deserializing from DynamoDB - /// (FromItem method). + /// (ToModel method). /// FromModel, } diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/DynamoFieldVerifyTests.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/DynamoFieldVerifyTests.cs index 2180045..16473f0 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/DynamoFieldVerifyTests.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/DynamoFieldVerifyTests.cs @@ -32,9 +32,9 @@ namespace MyNamespace; )] internal static partial class ExampleEntityMapper { - internal static partial Dictionary ToItem(ExampleEntity source); + internal static partial Dictionary FromModel(ExampleEntity source); - internal static partial ExampleEntity FromItem(Dictionary item); + internal static partial ExampleEntity ToModel(Dictionary item); internal static AttributeValue ToMethod(ExampleEntity source) => new() { S = source.String }; @@ -68,9 +68,9 @@ namespace MyNamespace; [DynamoField(nameof(ExampleEntity.NullableString), Required = true)] internal static partial class ExampleEntityMapper { - internal static partial Dictionary ToItem(ExampleEntity source); + internal static partial Dictionary FromModel(ExampleEntity source); - internal static partial ExampleEntity FromItem(Dictionary item); + internal static partial ExampleEntity ToModel(Dictionary item); } internal class ExampleEntity @@ -99,9 +99,9 @@ namespace MyNamespace; [DynamoField(nameof(ExampleEntity.String), Required = false)] internal static partial class ExampleEntityMapper { - internal static partial Dictionary ToItem(ExampleEntity source); + internal static partial Dictionary FromModel(ExampleEntity source); - internal static partial ExampleEntity FromItem(Dictionary item); + internal static partial ExampleEntity ToModel(Dictionary item); } internal class ExampleEntity @@ -130,9 +130,9 @@ namespace MyNamespace; [DynamoField(nameof(ExampleEntity.String), Kind = DynamoKind.B)] internal static partial class ExampleEntityMapper { - internal static partial Dictionary ToItem(ExampleEntity source); + internal static partial Dictionary FromModel(ExampleEntity source); - internal static partial ExampleEntity FromItem(Dictionary item); + internal static partial ExampleEntity ToModel(Dictionary item); } internal class ExampleEntity @@ -161,9 +161,9 @@ namespace MyNamespace; [DynamoField(nameof(ExampleEntity.String), OmitIfNull = false)] internal static partial class ExampleEntityMapper { - internal static partial Dictionary ToItem(ExampleEntity source); + internal static partial Dictionary FromModel(ExampleEntity source); - internal static partial ExampleEntity FromItem(Dictionary item); + internal static partial ExampleEntity ToModel(Dictionary item); } internal class ExampleEntity @@ -192,9 +192,9 @@ namespace MyNamespace; [DynamoField(nameof(ExampleEntity.String), OmitIfEmptyString = true)] internal static partial class ExampleEntityMapper { - internal static partial Dictionary ToItem(ExampleEntity source); + internal static partial Dictionary FromModel(ExampleEntity source); - internal static partial ExampleEntity FromItem(Dictionary item); + internal static partial ExampleEntity ToModel(Dictionary item); } internal class ExampleEntity @@ -223,9 +223,9 @@ namespace MyNamespace; [DynamoField(nameof(ExampleEntity.String), ToMethod = nameof(ToMethod))] internal static partial class ExampleEntityMapper { - internal static partial Dictionary ToItem(ExampleEntity source); + internal static partial Dictionary FromModel(ExampleEntity source); - internal static partial ExampleEntity FromItem(Dictionary item); + internal static partial ExampleEntity ToModel(Dictionary item); internal static AttributeValue ToMethod(ExampleEntity source) => new() { S = source.String }; } @@ -257,9 +257,9 @@ namespace MyNamespace; [DynamoField(nameof(ExampleEntity.String), FromMethod = nameof(FromMethod))] internal static partial class ExampleEntityMapper { - internal static partial Dictionary ToItem(ExampleEntity source); + internal static partial Dictionary FromModel(ExampleEntity source); - internal static partial ExampleEntity FromItem(Dictionary item); + internal static partial ExampleEntity ToModel(Dictionary item); internal static string FromMethod(Dictionary item) => item["customName"].S; @@ -295,9 +295,9 @@ namespace MyNamespace; )] internal static partial class ExampleEntityMapper { - internal static partial Dictionary ToItem(ExampleEntity source); + internal static partial Dictionary FromModel(ExampleEntity source); - internal static partial ExampleEntity FromItem(Dictionary item); + internal static partial ExampleEntity ToModel(Dictionary item); internal static Person PersonFromAttr(Dictionary item) => new(item["person"].S); diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/DynamoIgnoreVerifyTests.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/DynamoIgnoreVerifyTests.cs index cdf677d..11a79ca 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/DynamoIgnoreVerifyTests.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/DynamoIgnoreVerifyTests.cs @@ -18,13 +18,13 @@ namespace MyNamespace; [DynamoMapper] [DynamoIgnore(nameof(ExampleEntity.Ignore))] [DynamoIgnore(nameof(ExampleEntity.IgnoreAll), Ignore = IgnoreMapping.All)] - [DynamoIgnore(nameof(ExampleEntity.IgnoreInToItem), Ignore = IgnoreMapping.FromModel)] - [DynamoIgnore(nameof(ExampleEntity.IgnoreInFromItem), Ignore = IgnoreMapping.ToModel)] + [DynamoIgnore(nameof(ExampleEntity.IgnoreInFromModel), Ignore = IgnoreMapping.FromModel)] + [DynamoIgnore(nameof(ExampleEntity.IgnoreInToModel), Ignore = IgnoreMapping.ToModel)] internal static partial class ExampleEntityMapper { - internal static partial Dictionary ToItem(ExampleEntity source); + internal static partial Dictionary FromModel(ExampleEntity source); - internal static partial ExampleEntity FromItem(Dictionary item); + internal static partial ExampleEntity ToModel(Dictionary item); } internal class ExampleEntity @@ -32,8 +32,8 @@ internal class ExampleEntity internal required string String { get; set; } internal Type Ignore { get; set; } = typeof(ExampleEntity); internal string IgnoreAll { get; set; } = string.Empty; - internal string IgnoreInToItem { get; set; } = string.Empty; - internal string IgnoreInFromItem { get; set; } = string.Empty; + internal string IgnoreInFromModel { get; set; } = string.Empty; + internal string IgnoreInToModel { get; set; } = string.Empty; } """, }, diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/SimpleVerifyTests.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/SimpleVerifyTests.cs index 7bf3897..0fb6a38 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/SimpleVerifyTests.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/SimpleVerifyTests.cs @@ -17,9 +17,9 @@ namespace MyNamespace; [DynamoMapper] public static partial class ExampleEntityMapper { - public static partial Dictionary ToItem(MyDto source); + public static partial Dictionary FromModel(MyDto source); - public static partial MyDto FromItem(Dictionary item); + public static partial MyDto ToModel(Dictionary item); } public class MyDto @@ -46,9 +46,9 @@ namespace MyNamespace; [DynamoMapper] public static partial class ExampleEntityMapper { - public static partial Dictionary ToItem(MyDto source); + public static partial Dictionary FromModel(MyDto source); - public static partial MyDto FromItem(Dictionary item); + public static partial MyDto ToModel(Dictionary item); } public record MyDto @@ -75,9 +75,9 @@ namespace MyNamespace; [DynamoMapper] public static partial class ExampleEntityMapper { - public static partial Dictionary ToItem(MyDto source); + public static partial Dictionary FromModel(MyDto source); - public static partial MyDto FromItem(Dictionary item); + public static partial MyDto ToModel(Dictionary item); } public class MyDto @@ -104,9 +104,9 @@ namespace MyNamespace; [DynamoMapper] public static partial class ExampleEntityMapper { - public static partial Dictionary ToItem(ExampleEntity source); + public static partial Dictionary FromModel(ExampleEntity source); - public static partial ExampleEntity FromItem(Dictionary x); + public static partial ExampleEntity ToModel(Dictionary x); } public class ExampleEntity @@ -163,9 +163,9 @@ namespace MyNamespace; [DynamoMapper] public static partial class ExampleEntityMapper { - public static partial Dictionary ToItem(MyDto source); + public static partial Dictionary FromModel(MyDto source); - public static partial MyDto FromItem(Dictionary x); + public static partial MyDto ToModel(Dictionary x); } public class MyDto @@ -192,9 +192,9 @@ namespace MyNamespace; [DynamoMapper] public static partial class ExampleEntityMapper { - public static partial Dictionary ToItem(MyDto source); + public static partial Dictionary FromModel(MyDto source); - public static partial MyDto FromItem(Dictionary x); + public static partial MyDto ToModel(Dictionary x); } public class MyDto @@ -221,9 +221,9 @@ namespace MyNamespace; [DynamoMapper] public static partial class ExampleEntityMapper { - public static partial Dictionary ToAttributeValues(MyDto source); + public static partial Dictionary FromMyDto(MyDto source); - public static partial MyDto FromAttributeValues(Dictionary x); + public static partial MyDto ToMyDto(Dictionary x); } public class MyDto @@ -258,9 +258,9 @@ namespace MyNamespace; )] public static partial class ExampleEntityMapper { - public static partial Dictionary ToItem(ExampleEntity source); + public static partial Dictionary FromModel(ExampleEntity source); - public static partial ExampleEntity FromItem(Dictionary x); + public static partial ExampleEntity ToModel(Dictionary x); } public class ExampleEntity @@ -317,9 +317,9 @@ namespace MyNamespace; [DynamoMapper] public static partial class ExampleEntityMapper { - public static partial Dictionary ToItem(MyDto source); + public static partial Dictionary FromModel(MyDto source); - public static partial MyDto FromItem(Dictionary item); + public static partial MyDto ToModel(Dictionary item); } public class MyDto @@ -349,7 +349,7 @@ namespace MyNamespace; [DynamoMapper] public static partial class ExampleEntityMapper { - public static partial MyDto FromItem(Dictionary item); + public static partial MyDto ToModel(Dictionary item); } public class MyDto @@ -379,7 +379,7 @@ namespace MyNamespace; [DynamoField(nameof(MyDto.ShouldNotBeMapped), FromMethod = nameof(GetType))] public static partial class ExampleMyDtoMapper { - public static partial MyDto FromItem(Dictionary item); + public static partial MyDto ToModel(Dictionary item); public static Type GetType(Dictionary item) { @@ -414,7 +414,7 @@ namespace MyNamespace; [DynamoField(nameof(MyDto.ShouldNotBeMapped), ToMethod = nameof(SetType))] public static partial class ExampleMyDtoMapper { - public static partial Dictionary ToItem(MyDto source); + public static partial Dictionary FromModel(MyDto source); public static AttributeValue SetType(MyDto source) { diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_FromAndToMethod_CustomType#ExampleEntityMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_FromAndToMethod_CustomType#ExampleEntityMapper.g.verified.cs index 52fe222..2f6d03f 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_FromAndToMethod_CustomType#ExampleEntityMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_FromAndToMethod_CustomType#ExampleEntityMapper.g.verified.cs @@ -19,14 +19,14 @@ namespace MyNamespace; internal static partial class ExampleEntityMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - internal static partial global::System.Collections.Generic.Dictionary ToItem(global::MyNamespace.ExampleEntity source) => + internal static partial global::System.Collections.Generic.Dictionary FromModel(global::MyNamespace.ExampleEntity source) => new Dictionary(3) .SetString("string", source.String, false, true) .SetString("nullableString", source.NullableString, false, true) .Set("person", PersonToAttr(source)); [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - internal static partial global::MyNamespace.ExampleEntity FromItem(global::System.Collections.Generic.Dictionary item) => + internal static partial global::MyNamespace.ExampleEntity ToModel(global::System.Collections.Generic.Dictionary item) => new global::MyNamespace.ExampleEntity { String = item.GetString("string", Requiredness.InferFromNullability), diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_FromMethod#ExampleEntityMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_FromMethod#ExampleEntityMapper.g.verified.cs index 486ac0e..631c1b5 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_FromMethod#ExampleEntityMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_FromMethod#ExampleEntityMapper.g.verified.cs @@ -19,13 +19,13 @@ namespace MyNamespace; internal static partial class ExampleEntityMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - internal static partial global::System.Collections.Generic.Dictionary ToItem(global::MyNamespace.ExampleEntity source) => + internal static partial global::System.Collections.Generic.Dictionary FromModel(global::MyNamespace.ExampleEntity source) => new Dictionary(2) .SetString("string", source.String, false, true) .SetString("nullableString", source.NullableString, false, true); [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - internal static partial global::MyNamespace.ExampleEntity FromItem(global::System.Collections.Generic.Dictionary item) => + internal static partial global::MyNamespace.ExampleEntity ToModel(global::System.Collections.Generic.Dictionary item) => new global::MyNamespace.ExampleEntity { String = FromMethod(item), diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_KindOverride#ExampleEntityMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_KindOverride#ExampleEntityMapper.g.verified.cs index 869501b..e026e16 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_KindOverride#ExampleEntityMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_KindOverride#ExampleEntityMapper.g.verified.cs @@ -19,13 +19,13 @@ namespace MyNamespace; internal static partial class ExampleEntityMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - internal static partial global::System.Collections.Generic.Dictionary ToItem(global::MyNamespace.ExampleEntity source) => + internal static partial global::System.Collections.Generic.Dictionary FromModel(global::MyNamespace.ExampleEntity source) => new Dictionary(2) .SetString("string", source.String, false, true, DynamoKind.B) .SetString("nullableString", source.NullableString, false, true); [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - internal static partial global::MyNamespace.ExampleEntity FromItem(global::System.Collections.Generic.Dictionary item) => + internal static partial global::MyNamespace.ExampleEntity ToModel(global::System.Collections.Generic.Dictionary item) => new global::MyNamespace.ExampleEntity { String = item.GetString("string", Requiredness.InferFromNullability, DynamoKind.B), diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_NotNullableOptional#ExampleEntityMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_NotNullableOptional#ExampleEntityMapper.g.verified.cs index 0d9453d..467bd3f 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_NotNullableOptional#ExampleEntityMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_NotNullableOptional#ExampleEntityMapper.g.verified.cs @@ -19,13 +19,13 @@ namespace MyNamespace; internal static partial class ExampleEntityMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - internal static partial global::System.Collections.Generic.Dictionary ToItem(global::MyNamespace.ExampleEntity source) => + internal static partial global::System.Collections.Generic.Dictionary FromModel(global::MyNamespace.ExampleEntity source) => new Dictionary(2) .SetString("string", source.String, false, true) .SetString("nullableString", source.NullableString, false, true); [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - internal static partial global::MyNamespace.ExampleEntity FromItem(global::System.Collections.Generic.Dictionary item) => + internal static partial global::MyNamespace.ExampleEntity ToModel(global::System.Collections.Generic.Dictionary item) => new global::MyNamespace.ExampleEntity { String = item.GetString("string", Requiredness.Optional), diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_NullableFieldRequired#ExampleEntityMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_NullableFieldRequired#ExampleEntityMapper.g.verified.cs index 265b128..0b98893 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_NullableFieldRequired#ExampleEntityMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_NullableFieldRequired#ExampleEntityMapper.g.verified.cs @@ -19,13 +19,13 @@ namespace MyNamespace; internal static partial class ExampleEntityMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - internal static partial global::System.Collections.Generic.Dictionary ToItem(global::MyNamespace.ExampleEntity source) => + internal static partial global::System.Collections.Generic.Dictionary FromModel(global::MyNamespace.ExampleEntity source) => new Dictionary(2) .SetString("string", source.String, false, true) .SetString("nullableString", source.NullableString, false, true); [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - internal static partial global::MyNamespace.ExampleEntity FromItem(global::System.Collections.Generic.Dictionary item) => + internal static partial global::MyNamespace.ExampleEntity ToModel(global::System.Collections.Generic.Dictionary item) => new global::MyNamespace.ExampleEntity { String = item.GetString("string", Requiredness.InferFromNullability), diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_OmitIfEmptyStringOverride#ExampleEntityMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_OmitIfEmptyStringOverride#ExampleEntityMapper.g.verified.cs index 55a9326..0ae67be 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_OmitIfEmptyStringOverride#ExampleEntityMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_OmitIfEmptyStringOverride#ExampleEntityMapper.g.verified.cs @@ -19,13 +19,13 @@ namespace MyNamespace; internal static partial class ExampleEntityMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - internal static partial global::System.Collections.Generic.Dictionary ToItem(global::MyNamespace.ExampleEntity source) => + internal static partial global::System.Collections.Generic.Dictionary FromModel(global::MyNamespace.ExampleEntity source) => new Dictionary(2) .SetString("string", source.String, true, true) .SetString("nullableString", source.NullableString, false, true); [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - internal static partial global::MyNamespace.ExampleEntity FromItem(global::System.Collections.Generic.Dictionary item) => + internal static partial global::MyNamespace.ExampleEntity ToModel(global::System.Collections.Generic.Dictionary item) => new global::MyNamespace.ExampleEntity { String = item.GetString("string", Requiredness.InferFromNullability), diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_OmitIfNullOverride#ExampleEntityMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_OmitIfNullOverride#ExampleEntityMapper.g.verified.cs index e4b9963..e877c53 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_OmitIfNullOverride#ExampleEntityMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_OmitIfNullOverride#ExampleEntityMapper.g.verified.cs @@ -19,13 +19,13 @@ namespace MyNamespace; internal static partial class ExampleEntityMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - internal static partial global::System.Collections.Generic.Dictionary ToItem(global::MyNamespace.ExampleEntity source) => + internal static partial global::System.Collections.Generic.Dictionary FromModel(global::MyNamespace.ExampleEntity source) => new Dictionary(2) .SetString("string", source.String, false, false) .SetString("nullableString", source.NullableString, false, true); [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - internal static partial global::MyNamespace.ExampleEntity FromItem(global::System.Collections.Generic.Dictionary item) => + internal static partial global::MyNamespace.ExampleEntity ToModel(global::System.Collections.Generic.Dictionary item) => new global::MyNamespace.ExampleEntity { String = item.GetString("string", Requiredness.InferFromNullability), diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_Simple#ExampleEntityMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_Simple#ExampleEntityMapper.g.verified.cs index 76a4df4..910a7fc 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_Simple#ExampleEntityMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_Simple#ExampleEntityMapper.g.verified.cs @@ -19,13 +19,13 @@ namespace MyNamespace; internal static partial class ExampleEntityMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - internal static partial global::System.Collections.Generic.Dictionary ToItem(global::MyNamespace.ExampleEntity source) => + internal static partial global::System.Collections.Generic.Dictionary FromModel(global::MyNamespace.ExampleEntity source) => new Dictionary(2) .Set("customName", ToMethod(source)) .SetString("ANOTHER_NAME", source.NullableString, true, false, DynamoKind.N); [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - internal static partial global::MyNamespace.ExampleEntity FromItem(global::System.Collections.Generic.Dictionary item) => + internal static partial global::MyNamespace.ExampleEntity ToModel(global::System.Collections.Generic.Dictionary item) => new global::MyNamespace.ExampleEntity { String = FromMethod(item), diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_ToMethod#ExampleEntityMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_ToMethod#ExampleEntityMapper.g.verified.cs index ea1bd1f..80ca697 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_ToMethod#ExampleEntityMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoFieldVerifyTests.DynamoField_ToMethod#ExampleEntityMapper.g.verified.cs @@ -19,13 +19,13 @@ namespace MyNamespace; internal static partial class ExampleEntityMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - internal static partial global::System.Collections.Generic.Dictionary ToItem(global::MyNamespace.ExampleEntity source) => + internal static partial global::System.Collections.Generic.Dictionary FromModel(global::MyNamespace.ExampleEntity source) => new Dictionary(2) .Set("string", ToMethod(source)) .SetString("nullableString", source.NullableString, false, true); [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - internal static partial global::MyNamespace.ExampleEntity FromItem(global::System.Collections.Generic.Dictionary item) => + internal static partial global::MyNamespace.ExampleEntity ToModel(global::System.Collections.Generic.Dictionary item) => new global::MyNamespace.ExampleEntity { String = item.GetString("string", Requiredness.InferFromNullability), diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoIgnoreVerifyTests.DynamoIgnore_Simple#ExampleEntityMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoIgnoreVerifyTests.DynamoIgnore_Simple#ExampleEntityMapper.g.verified.cs index d6a6cc1..1424dc1 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoIgnoreVerifyTests.DynamoIgnore_Simple#ExampleEntityMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/DynamoIgnoreVerifyTests.DynamoIgnore_Simple#ExampleEntityMapper.g.verified.cs @@ -19,16 +19,16 @@ namespace MyNamespace; internal static partial class ExampleEntityMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - internal static partial global::System.Collections.Generic.Dictionary ToItem(global::MyNamespace.ExampleEntity source) => + internal static partial global::System.Collections.Generic.Dictionary FromModel(global::MyNamespace.ExampleEntity source) => new Dictionary(2) .SetString("string", source.String, false, true) - .SetString("ignoreInFromItem", source.IgnoreInFromItem, false, true); + .SetString("ignoreInToModel", source.IgnoreInToModel, false, true); [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - internal static partial global::MyNamespace.ExampleEntity FromItem(global::System.Collections.Generic.Dictionary item) => + internal static partial global::MyNamespace.ExampleEntity ToModel(global::System.Collections.Generic.Dictionary item) => new global::MyNamespace.ExampleEntity { String = item.GetString("string", Requiredness.InferFromNullability), - IgnoreInToItem = item.GetString("ignoreInToItem", Requiredness.InferFromNullability), + IgnoreInFromModel = item.GetString("ignoreInFromModel", Requiredness.InferFromNullability), }; } diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_AllHelperTypes#ExampleEntityMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_AllHelperTypes#ExampleEntityMapper.g.verified.cs index 5b07afe..13fec5e 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_AllHelperTypes#ExampleEntityMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_AllHelperTypes#ExampleEntityMapper.g.verified.cs @@ -19,7 +19,7 @@ namespace MyNamespace; public static partial class ExampleEntityMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - public static partial global::System.Collections.Generic.Dictionary ToItem(global::MyNamespace.ExampleEntity source) => + public static partial global::System.Collections.Generic.Dictionary FromModel(global::MyNamespace.ExampleEntity source) => new Dictionary(22) .SetBool("bool", source.Bool, false, true) .SetBool("nullableBool", source.NullableBool, false, true) @@ -45,7 +45,7 @@ public static partial class ExampleEntityMapper .SetEnum("nullableEnum", source.NullableEnum, "G", false, true); [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - public static partial global::MyNamespace.ExampleEntity FromItem(global::System.Collections.Generic.Dictionary x) => + public static partial global::MyNamespace.ExampleEntity ToModel(global::System.Collections.Generic.Dictionary x) => new global::MyNamespace.ExampleEntity { Bool = x.GetBool("bool", Requiredness.InferFromNullability), diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_AllOptionsSetToNonDefaultValues#ExampleEntityMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_AllOptionsSetToNonDefaultValues#ExampleEntityMapper.g.verified.cs index e86d10f..c74c050 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_AllOptionsSetToNonDefaultValues#ExampleEntityMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_AllOptionsSetToNonDefaultValues#ExampleEntityMapper.g.verified.cs @@ -19,7 +19,7 @@ namespace MyNamespace; public static partial class ExampleEntityMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - public static partial global::System.Collections.Generic.Dictionary ToItem(global::MyNamespace.ExampleEntity source) => + public static partial global::System.Collections.Generic.Dictionary FromModel(global::MyNamespace.ExampleEntity source) => new Dictionary(22) .SetBool("bool", source.Bool, true, false) .SetBool("nullable_bool", source.NullableBool, true, false) @@ -45,7 +45,7 @@ public static partial class ExampleEntityMapper .SetEnum("nullable_enum", source.NullableEnum, "G", true, false); [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - public static partial global::MyNamespace.ExampleEntity FromItem(global::System.Collections.Generic.Dictionary x) => + public static partial global::MyNamespace.ExampleEntity ToModel(global::System.Collections.Generic.Dictionary x) => new global::MyNamespace.ExampleEntity { Bool = x.GetBool("bool", Requiredness.Required), diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_EmptyModel#ExampleEntityMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_EmptyModel#ExampleEntityMapper.g.verified.cs index 36fbfc6..44c1430 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_EmptyModel#ExampleEntityMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_EmptyModel#ExampleEntityMapper.g.verified.cs @@ -19,11 +19,11 @@ namespace MyNamespace; public static partial class ExampleEntityMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - public static partial global::System.Collections.Generic.Dictionary ToItem(global::MyNamespace.MyDto source) => + public static partial global::System.Collections.Generic.Dictionary FromModel(global::MyNamespace.MyDto source) => new Dictionary(0); [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - public static partial global::MyNamespace.MyDto FromItem(global::System.Collections.Generic.Dictionary item) => + public static partial global::MyNamespace.MyDto ToModel(global::System.Collections.Generic.Dictionary item) => new global::MyNamespace.MyDto { }; diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_HelloWorld#ExampleEntityMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_HelloWorld#ExampleEntityMapper.g.verified.cs index 6763653..5a29a13 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_HelloWorld#ExampleEntityMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_HelloWorld#ExampleEntityMapper.g.verified.cs @@ -19,12 +19,12 @@ namespace MyNamespace; public static partial class ExampleEntityMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - public static partial global::System.Collections.Generic.Dictionary ToItem(global::MyNamespace.MyDto source) => + public static partial global::System.Collections.Generic.Dictionary FromModel(global::MyNamespace.MyDto source) => new Dictionary(1) .SetString("name", source.Name, false, true); [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - public static partial global::MyNamespace.MyDto FromItem(global::System.Collections.Generic.Dictionary item) => + public static partial global::MyNamespace.MyDto ToModel(global::System.Collections.Generic.Dictionary item) => new global::MyNamespace.MyDto { Name = item.GetString("name", Requiredness.InferFromNullability), diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_InitProperty#ExampleEntityMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_InitProperty#ExampleEntityMapper.g.verified.cs index 303e250..7a11bb0 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_InitProperty#ExampleEntityMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_InitProperty#ExampleEntityMapper.g.verified.cs @@ -19,12 +19,12 @@ namespace MyNamespace; public static partial class ExampleEntityMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - public static partial global::System.Collections.Generic.Dictionary ToItem(global::MyNamespace.MyDto source) => + public static partial global::System.Collections.Generic.Dictionary FromModel(global::MyNamespace.MyDto source) => new Dictionary(1) .SetString("name", source.Name, false, true); [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - public static partial global::MyNamespace.MyDto FromItem(global::System.Collections.Generic.Dictionary x) => + public static partial global::MyNamespace.MyDto ToModel(global::System.Collections.Generic.Dictionary x) => new global::MyNamespace.MyDto { Name = x.GetString("name", Requiredness.InferFromNullability), diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_MethodNamePrefixWorks#ExampleEntityMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_MethodNamePrefixWorks#ExampleEntityMapper.g.verified.cs index fc68cf8..bb2ace7 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_MethodNamePrefixWorks#ExampleEntityMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_MethodNamePrefixWorks#ExampleEntityMapper.g.verified.cs @@ -19,12 +19,12 @@ namespace MyNamespace; public static partial class ExampleEntityMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - public static partial global::System.Collections.Generic.Dictionary ToAttributeValues(global::MyNamespace.MyDto source) => + public static partial global::System.Collections.Generic.Dictionary FromMyDto(global::MyNamespace.MyDto source) => new Dictionary(1) .SetString("name", source.Name, false, true); [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - public static partial global::MyNamespace.MyDto FromAttributeValues(global::System.Collections.Generic.Dictionary x) => + public static partial global::MyNamespace.MyDto ToMyDto(global::System.Collections.Generic.Dictionary x) => new global::MyNamespace.MyDto { Name = x.GetString("name", Requiredness.InferFromNullability), diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_NoSetter#ExampleEntityMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_NoSetter#ExampleEntityMapper.g.verified.cs index d1aa0cb..c349c09 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_NoSetter#ExampleEntityMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_NoSetter#ExampleEntityMapper.g.verified.cs @@ -19,12 +19,12 @@ namespace MyNamespace; public static partial class ExampleEntityMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - public static partial global::System.Collections.Generic.Dictionary ToItem(global::MyNamespace.MyDto source) => + public static partial global::System.Collections.Generic.Dictionary FromModel(global::MyNamespace.MyDto source) => new Dictionary(1) .SetString("name", source.Name, false, true); [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - public static partial global::MyNamespace.MyDto FromItem(global::System.Collections.Generic.Dictionary x) => + public static partial global::MyNamespace.MyDto ToModel(global::System.Collections.Generic.Dictionary x) => new global::MyNamespace.MyDto { }; diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_NoToMethod#ExampleEntityMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_NoToMethod#ExampleEntityMapper.g.verified.cs index 96cd305..7b252d1 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_NoToMethod#ExampleEntityMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_NoToMethod#ExampleEntityMapper.g.verified.cs @@ -19,7 +19,7 @@ namespace MyNamespace; public static partial class ExampleEntityMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - public static partial global::MyNamespace.MyDto FromItem(global::System.Collections.Generic.Dictionary item) => + public static partial global::MyNamespace.MyDto ToModel(global::System.Collections.Generic.Dictionary item) => new global::MyNamespace.MyDto { Name = item.GetString("name", Requiredness.InferFromNullability), diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_OverrideOnlyOnFromMethod_NoToMethod#ExampleMyDtoMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_OverrideOnlyOnFromMethod_NoToMethod#ExampleMyDtoMapper.g.verified.cs index 9c6218a..d174bed 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_OverrideOnlyOnFromMethod_NoToMethod#ExampleMyDtoMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_OverrideOnlyOnFromMethod_NoToMethod#ExampleMyDtoMapper.g.verified.cs @@ -19,7 +19,7 @@ namespace MyNamespace; public static partial class ExampleMyDtoMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - public static partial global::MyNamespace.MyDto FromItem(global::System.Collections.Generic.Dictionary item) => + public static partial global::MyNamespace.MyDto ToModel(global::System.Collections.Generic.Dictionary item) => new global::MyNamespace.MyDto { Name = item.GetString("name", Requiredness.InferFromNullability), diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_OverrideOnlyOnToMethod_NoFromMethod#ExampleMyDtoMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_OverrideOnlyOnToMethod_NoFromMethod#ExampleMyDtoMapper.g.verified.cs index e927f70..a4dd725 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_OverrideOnlyOnToMethod_NoFromMethod#ExampleMyDtoMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_OverrideOnlyOnToMethod_NoFromMethod#ExampleMyDtoMapper.g.verified.cs @@ -19,7 +19,7 @@ namespace MyNamespace; public static partial class ExampleMyDtoMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - public static partial global::System.Collections.Generic.Dictionary ToItem(global::MyNamespace.MyDto source) => + public static partial global::System.Collections.Generic.Dictionary FromModel(global::MyNamespace.MyDto source) => new Dictionary(2) .SetString("name", source.Name, false, true) .Set("shouldNotBeMapped", SetType(source)); diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_PropertiesWithNoSetter#ExampleEntityMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_PropertiesWithNoSetter#ExampleEntityMapper.g.verified.cs index 556324e..e0c844e 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_PropertiesWithNoSetter#ExampleEntityMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_PropertiesWithNoSetter#ExampleEntityMapper.g.verified.cs @@ -19,14 +19,14 @@ namespace MyNamespace; public static partial class ExampleEntityMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - public static partial global::System.Collections.Generic.Dictionary ToItem(global::MyNamespace.MyDto source) => + public static partial global::System.Collections.Generic.Dictionary FromModel(global::MyNamespace.MyDto source) => new Dictionary(3) .SetString("name", source.Name, false, true) .SetString("readOnlyString", source.ReadOnlyString, false, true) .SetString("expressionProperty", source.ExpressionProperty, false, true); [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - public static partial global::MyNamespace.MyDto FromItem(global::System.Collections.Generic.Dictionary item) => + public static partial global::MyNamespace.MyDto ToModel(global::System.Collections.Generic.Dictionary item) => new global::MyNamespace.MyDto { Name = item.GetString("name", Requiredness.InferFromNullability), diff --git a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_Record#ExampleEntityMapper.g.verified.cs b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_Record#ExampleEntityMapper.g.verified.cs index 6763653..5a29a13 100644 --- a/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_Record#ExampleEntityMapper.g.verified.cs +++ b/test/LayeredCraft.DynamoMapper.Generators.Tests/Snapshots/SimpleVerifyTests.Simple_Record#ExampleEntityMapper.g.verified.cs @@ -19,12 +19,12 @@ namespace MyNamespace; public static partial class ExampleEntityMapper { [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - public static partial global::System.Collections.Generic.Dictionary ToItem(global::MyNamespace.MyDto source) => + public static partial global::System.Collections.Generic.Dictionary FromModel(global::MyNamespace.MyDto source) => new Dictionary(1) .SetString("name", source.Name, false, true); [global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "REPLACED")] - public static partial global::MyNamespace.MyDto FromItem(global::System.Collections.Generic.Dictionary item) => + public static partial global::MyNamespace.MyDto ToModel(global::System.Collections.Generic.Dictionary item) => new global::MyNamespace.MyDto { Name = item.GetString("name", Requiredness.InferFromNullability), From 48ed84fffd478cf8e40b036ec804b82f98857ddd Mon Sep 17 00:00:00 2001 From: Jonas Ha Date: Thu, 15 Jan 2026 08:32:33 -0500 Subject: [PATCH 2/2] fix(mapper): correct incorrect enum member format in strategy resolver - Fixed improper formatting in `TypeMappingStrategyResolver` for nullable enums. - Resolved misplacement of enum name in the expression. --- .../PropertyMapping/TypeMappingStrategyResolver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LayeredCraft.DynamoMapper.Generators/PropertyMapping/TypeMappingStrategyResolver.cs b/src/LayeredCraft.DynamoMapper.Generators/PropertyMapping/TypeMappingStrategyResolver.cs index f5f0f06..78bc322 100644 --- a/src/LayeredCraft.DynamoMapper.Generators/PropertyMapping/TypeMappingStrategyResolver.cs +++ b/src/LayeredCraft.DynamoMapper.Generators/PropertyMapping/TypeMappingStrategyResolver.cs @@ -199,7 +199,7 @@ GeneratorContext context // Nullable enums don't need a default value (they can be null) string[] fromArgs = analysis.Nullability.IsNullableType ? [enumFormat] -mType.MemberNames.First()}", enumFormat]; + : [$"{enumName}.{enumType.MemberNames.First()}", enumFormat]; string[] toArgs = [enumFormat];