Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MappingTarget attribute to set the mapping target as the first parameter #1376

Merged
merged 1 commit into from
Jul 4, 2024
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
2 changes: 1 addition & 1 deletion docs/docs/configuration/derived-type-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ description: Map derived types and interfaces
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

Mapperly supports interfaces and base types as mapping sources and targets, for both new instance and [existing target](./existing-target.md) mappings.
Mapperly supports interfaces and base types as mapping sources and targets, for both new instance and [existing target](./existing-target.mdx) mappings.
To do this, Mapperly needs to know which derived types exist.
This can be configured with the `MapDerivedTypeAttribute`:

Expand Down
76 changes: 0 additions & 76 deletions docs/docs/configuration/existing-target.md

This file was deleted.

114 changes: 114 additions & 0 deletions docs/docs/configuration/existing-target.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
sidebar_position: 9
description: Map to an existing target object
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Existing target object

If an existing object instance should be used as target, you can define the mapping method as void with the target as second parameter:

<Tabs>
<TabItem value="declaration" label="Declaration" default>
```csharp
[Mapper]
public partial class CarMapper
{
// highlight-start
public partial void UpdateCarDto(Car car, CarDto dto);
// highlight-end
}
```
</TabItem>
<TabItem value="usage" label="Usage">
```csharp
var mapper = new CarMapper();
var car = new Car { NumberOfSeats = 10, ... };
var dto = new CarDto();

mapper.UpdateCarDto(car, dto);
dto.NumberOfSeats.Should().Be(10);
```
</TabItem>
</Tabs>

## Merge objects

To merge two objects together, `AllowNullPropertyAssignment` can be set to `false`.
This ignores all properties on the source with a `null` value.

<Tabs>
<TabItem value="declaration" label="Declaration" default>
```csharp
// highlight-start
[Mapper(AllowNullPropertyAssignment = false)]
// highlight-end
static partial class FruitMapper
{
// highlight-start
public static partial void ApplyUpdate(FruitUpdate update, Fruit fruit);
// highlight-end
}

class Fruit { public required string Name { get; set; } public required string Color { get; set; } }
record FruitUpdate(string? Name, string? Color);
```
</TabItem>
<TabItem value="usage" label="Usage">
```csharp
FruitMapper.ApplyUpdate(myUpdateRequest, myFruit);
```
</TabItem>
<TabItem value="generated" label="Generated code" default>
```csharp
static partial class FruitMapper
{
public static partial void Update(global::FruitUpdate update, global::Fruit fruit)
{
if (update.Name != null)
{
fruit.Name = update.Name;
}
if (update.Color != null)
{
fruit.Color = update.Color;
}
}
}
```
</TabItem>
</Tabs>

See also [null value handling](./mapper.mdx#null-values).

The `MappingTarget` attribute allows setting the first method parameter as mapping target:

<Tabs>
<TabItem value="declaration" label="Declaration" default>

```csharp
// highlight-start
[Mapper(AllowNullPropertyAssignment = false)]
// highlight-end
static partial class FruitMapper
{
// highlight-start
public static partial void ApplyUpdate([MappingTarget] this Fruit fruit, FruitUpdate update);
// highlight-end
}

class Fruit { public required string Name { get; set; } public required string Color { get; set; } }
record FruitUpdate(string? Name, string? Color);
```

</TabItem>
<TabItem value="usage" label="Usage">
```csharp
myFruit.ApplyUpdate(myUpdateRequest);
```
</TabItem>
</Tabs>

See also [extension methods](./static-mappers.md).
2 changes: 1 addition & 1 deletion docs/docs/configuration/static-mappers.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Mapperly supports static mappers and extension methods:
[Mapper]
public static partial class CarMapper
{
public static partial CarDto CarToCarDto(this Car car);
public static partial CarDto ToCarDto(this Car car);

private static int TimeSpanToHours(TimeSpan t) => t.Hours;
}
Expand Down
10 changes: 10 additions & 0 deletions src/Riok.Mapperly.Abstractions/MappingTargetAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Diagnostics;

namespace Riok.Mapperly.Abstractions;

/// <summary>
/// Marks a given parameter as the mapping target.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
[Conditional("MAPPERLY_ABSTRACTIONS_SCOPE_RUNTIME")]
public sealed class MappingTargetAttribute : Attribute;
4 changes: 4 additions & 0 deletions src/Riok.Mapperly.Abstractions/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,7 @@ Riok.Mapperly.Abstractions.MapValueAttribute.MapValueAttribute(string![]! target
Riok.Mapperly.Abstractions.MapValueAttribute.Target.get -> System.Collections.Generic.IReadOnlyCollection<string!>!
Riok.Mapperly.Abstractions.MapValueAttribute.TargetFullName.get -> string!
Riok.Mapperly.Abstractions.MapValueAttribute.Value.get -> object?
Riok.Mapperly.Abstractions.MappingTargetAttribute
Riok.Mapperly.Abstractions.MappingTargetAttribute.MappingTargetAttribute() -> void
Riok.Mapperly.Abstractions.MapPropertyAttribute.MapPropertyAttribute(string! source, string![]! target) -> void
Riok.Mapperly.Abstractions.MapPropertyAttribute.MapPropertyAttribute(string![]! source, string! target) -> void
2 changes: 0 additions & 2 deletions src/Riok.Mapperly.Abstractions/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
#nullable enable
Riok.Mapperly.Abstractions.MapPropertyAttribute.MapPropertyAttribute(string! source, string![]! target) -> void
Riok.Mapperly.Abstractions.MapPropertyAttribute.MapPropertyAttribute(string![]! source, string! target) -> void
5 changes: 1 addition & 4 deletions src/Riok.Mapperly/Descriptors/SymbolAccessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,7 @@ public bool CanAssign(ITypeSymbol sourceType, ITypeSymbol targetType)
&& (targetType.IsNullable() || !sourceType.IsNullable());
}

public MethodParameter? WrapOptionalMethodParameter(IParameterSymbol? symbol)
{
return symbol == null ? null : WrapMethodParameter(symbol);
}
public MethodParameter? WrapOptionalMethodParameter(IParameterSymbol? symbol) => symbol == null ? null : WrapMethodParameter(symbol);

public MethodParameter WrapMethodParameter(IParameterSymbol symbol) => new(symbol, UpgradeNullable(symbol.Type));

Expand Down
Loading