Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,15 @@ public class Product
### 2. Create a mapper class

```csharp
using DynamoMapper.Attributes;
using DynamoMapper.Runtime;
using Amazon.DynamoDBv2.Model;

namespace MyApp.Data;

[DynamoMapper(Convention = DynamoNamingConvention.CamelCase)]
[DynamoField(nameof(Product.Description), OmitIfNull = true, OmitIfEmptyString = true)]
public static partial class ProductMapper
{
[DynamoField(nameof(Product.Description), OmitIfNullOrWhiteSpace = true)]
public static partial Dictionary<string, AttributeValue> ToItem(Product source);

public static partial Product FromItem(Dictionary<string, AttributeValue> item);
Expand Down Expand Up @@ -111,15 +111,15 @@ For more examples including single-table patterns and custom converters, see the

## Key Features

- **Attribute-Based Configuration** (Phase 1): Configure mapping via `[DynamoField]` and `[DynamoIgnore]` attributes on mapper methods
- **Attribute-Based Configuration** (Phase 1): Configure mapping via `[DynamoField]` and `[DynamoIgnore]` on mapper classes
- **Fluent DSL Configuration** (Phase 2): Optional strongly-typed DSL for configuration
- **Naming Conventions**: CamelCase, SnakeCase, or Exact field naming
- **Required Field Validation**: Compile-time and runtime validation of required fields
- **Omission Policies**: Flexible null/empty value omission strategies
- **Custom Converters**: Type-safe converter pattern for custom serialization
- **Customization Hooks**: Before/After hooks for injecting pk/sk and custom logic
- **Single-Table Support**: Built for single-table DynamoDB design patterns
- **Supported Types**: string, numeric types, bool, Guid, DateTime, DateTimeOffset, TimeSpan, enums
- **Supported Types**: strings, numerics, bool, Guid, DateTime, DateTimeOffset, TimeSpan, enums, plus lists/maps/sets of supported element types
- **Comprehensive Diagnostics**: Clear compile-time errors with actionable messages
- **Zero Configuration**: Sensible defaults for most use cases

Expand Down Expand Up @@ -155,4 +155,4 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file

---

**Made with ⚑ by the LayeredCraft team**
**Made with ⚑ by the LayeredCraft team**
63 changes: 62 additions & 1 deletion docs/api-reference/attributes.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,64 @@
# Attributes

Documentation coming soon. See [Phase 1 Requirements](../roadmap/phase-1.md) for detailed specifications.
DynamoMapper configuration is done via attributes on the **mapper class**.

## DynamoMapperAttribute

Marks a static partial class as a mapper and sets defaults.

```csharp
[DynamoMapper(
Convention = DynamoNamingConvention.CamelCase,
DefaultRequiredness = Requiredness.InferFromNullability,
OmitNullStrings = true,
OmitEmptyStrings = false,
DateTimeFormat = "O",
EnumFormat = "G")]
Comment on lines +15 to +16
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add guid and timespan formats here to if you want all props set?

public static partial class OrderMapper
{
public static partial Dictionary<string, AttributeValue> ToItem(Order source);
public static partial Order FromItem(Dictionary<string, AttributeValue> item);
}
```

## DynamoFieldAttribute

Configures mapping for a specific member. Apply multiple times to the mapper class.

```csharp
[DynamoMapper]
[DynamoField(nameof(Order.Notes), OmitIfNull = true, OmitIfEmptyString = true)]
[DynamoField(nameof(Order.OrderId), AttributeName = "orderId")]
public static partial class OrderMapper
{
public static partial Dictionary<string, AttributeValue> ToItem(Order source);
public static partial Order FromItem(Dictionary<string, AttributeValue> item);
}
```

Properties:
- `MemberName` (ctor) - target member name
- `AttributeName` - DynamoDB attribute name override
- `Required` - requiredness override
- `Kind` - DynamoDB `DynamoKind` override
- `OmitIfNull` - omit when null
- `OmitIfEmptyString` - omit when empty string
- `ToMethod` / `FromMethod` - static conversion methods on the mapper class

## DynamoIgnoreAttribute

Skips a member in one or both directions.

```csharp
[DynamoMapper]
[DynamoIgnore(nameof(Order.InternalNotes), Ignore = IgnoreMapping.All)]
public static partial class OrderMapper
{
public static partial Dictionary<string, AttributeValue> ToItem(Order source);
public static partial Order FromItem(Dictionary<string, AttributeValue> item);
}
```

Properties:
- `MemberName` (ctor) - target member name
- `Ignore` - `IgnoreMapping.All`, `IgnoreMapping.FromModel`, or `IgnoreMapping.ToModel`
46 changes: 45 additions & 1 deletion docs/api-reference/generated-code.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,47 @@
# Generated Code

Documentation coming soon. See [Phase 1 Requirements](../roadmap/phase-1.md) for detailed specifications.
The generator emits explicit `ToItem`/`FromItem` implementations that call the runtime
extension methods. The example below is a real-world output.

```csharp
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

#nullable enable

using DynamoMapper.Runtime;
using System.Collections.Generic;
using Amazon.DynamoDBv2.Model;

namespace Trivia.Manager.Shared.Models;

internal static partial class DuplicateResultMapper
{
[global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "1.0.0.0")]
internal static partial global::System.Collections.Generic.Dictionary<string, global::Amazon.DynamoDBv2.Model.AttributeValue> ToItem(global::Trivia.Manager.Shared.Models.DuplicateResult duplicateResult) =>
new Dictionary<string, AttributeValue>(2)
.SetString("id", duplicateResult.Id, false, true)
.Set("score", ScoreToAttribute(duplicateResult));

[global::System.CodeDom.Compiler.GeneratedCode("DynamoMapper", "1.0.0.0")]
internal static partial global::Trivia.Manager.Shared.Models.DuplicateResult FromItem(global::System.Collections.Generic.Dictionary<string, global::Amazon.DynamoDBv2.Model.AttributeValue> item)
{
var duplicateResult = new global::Trivia.Manager.Shared.Models.DuplicateResult
{
Id = item.GetString("id", Requiredness.InferFromNullability),
Score = item.GetFloat("score", Requiredness.InferFromNullability),
};
return duplicateResult;
}
}
```

Notes:
- Generated code uses `SetX`/`GetX` helpers from `DynamoMapper.Runtime`.
- Class-level `[DynamoField]`/`[DynamoIgnore]` configuration influences argument values.
74 changes: 73 additions & 1 deletion docs/api-reference/runtime-helpers.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,75 @@
# Runtime Helpers

Documentation coming soon. See [Phase 1 Requirements](../roadmap/phase-1.md) for detailed specifications.
The runtime package exposes extension methods on
`Dictionary<string, AttributeValue>` for both generated code and manual use.

## Core Value Helpers

These methods live in `DynamoMapper.Runtime` and are used by the generator.

String:
- `GetString`, `GetNullableString`
- `SetString`

Boolean:
- `GetBool`, `GetNullableBool`
- `SetBool`

Numbers:
- `GetInt`, `GetNullableInt`
- `GetLong`, `GetNullableLong`
- `GetFloat`, `GetNullableFloat`
- `GetDouble`, `GetNullableDouble`
- `GetDecimal`, `GetNullableDecimal`
- `GetShort`, `GetNullableShort`
- `GetByte`, `GetNullableByte`
- `SetInt`, `SetLong`, `SetFloat`, `SetDouble`, `SetDecimal`, `SetShort`, `SetByte`

Date/Time:
- `GetDateTime`, `GetNullableDateTime`
- `GetDateTimeOffset`, `GetNullableDateTimeOffset`
- `GetTimeSpan`, `GetNullableTimeSpan`
- `SetDateTime`, `SetDateTimeOffset`, `SetTimeSpan`

Guid:
- `GetGuid`, `GetNullableGuid`
- `SetGuid`

Enum:
- `GetEnum<T>`, `GetNullableEnum<T>`
- `SetEnum<T>`

## Collection Helpers

Lists and maps:
- `GetList<T>`, `GetNullableList<T>`
- `SetList<T>`
- `GetMap<T>`, `GetNullableMap<T>`
- `SetMap<T>`

Sets:
- `GetStringSet`, `GetNullableStringSet`, `SetStringSet`
- `GetNumberSet<T>`, `GetNullableNumberSet<T>`, `SetNumberSet<T>`
- `GetBinarySet`, `GetNullableBinarySet`, `SetBinarySet`

## Parameters

- `Requiredness` controls missing-attribute behavior (`Required`, `Optional`,
`InferFromNullability`).
- `DynamoKind` lets you override the inferred DynamoDB kind.

## Example

```csharp
var attributes = new Dictionary<string, AttributeValue>();

attributes
.SetString("pk", "USER#123")
.SetInt("version", 2)
.SetList("tags", new List<string> { "alpha", "beta" })
.SetMap("metadata", new Dictionary<string, int> { ["count"] = 42 })
.SetBinarySet("payloads", new List<byte[]> { new byte[] { 1, 2, 3 } });

var tags = attributes.GetList<string>("tags");
var version = attributes.GetInt("version");
```
Loading
Loading