Skip to content

Commit

Permalink
chore: move member path resolution to symbol accessor
Browse files Browse the repository at this point in the history
  • Loading branch information
latonz committed Jul 16, 2023
1 parent 3244a52 commit 32bfb80
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 147 deletions.
2 changes: 1 addition & 1 deletion docs/docs/configuration/enum.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The `IgnoreCase` property allows to opt in for case insensitive mappings (defaul
<!-- do not indent this, it won't work, https://stackoverflow.com/a/67579641/3302887 -->

<Tabs>
<TabItem value="global" label="Global (Mapper Level)" default>
<TabItem value="global" label="Global (mapper level)" default>

Applied to all enums mapped inside this mapper.

Expand Down
27 changes: 16 additions & 11 deletions docs/docs/configuration/mapper.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,25 @@ public partial class CarMapper
}
```

### Ignore obsolete members stratgey
### Ignore obsolete members

By default, mapperly will map source/target members marked with `ObsoleteAttribute`. This can be changed by setting the `IgnoreObsoleteMembersStrategy` of a method with `MapperIgnoreObsoleteMembersAttribute`, or by setting the `IgnoreObsoleteMembersStrategy` option of the `MapperAttribute`.
By default, Mapperly will map source/target members marked with `ObsoleteAttribute`.
This can be changed by setting the `IgnoreObsoleteMembersStrategy` of a method with `MapperIgnoreObsoleteMembersAttribute`,
or by setting the `IgnoreObsoleteMembersStrategy` option of the `MapperAttribute`.

| Name | Description |
| ------ | ------------------------------------------------------------------------------- |
| None | Will map members marked with the `Obsolete` attribute (default) |
| Both | Ignores source and target members that are mapped with the `Obsolete` attribute |
| Source | Ignores source members that are mapped with the `Obsolete` attribute |
| Target | Ignores target members that are mapped with the `Obsolete` attribute |
| Name | Description |
| ------ | --------------------------------------------------------------- |
| None | Will map members marked with the `Obsolete` attribute (default) |
| Both | Ignores source and target members with the `Obsolete` attribute |
| Source | Ignores source members with the `Obsolete` attribute |
| Target | Ignores target members with the `Obsolete` attribute |

<Tabs>
<TabItem value="global" label="Global (Mapper Level)" default>
<TabItem value="global" label="Global (mapper level)" default>

Sets the `IgnoreObsoleteMembersStrategy` for all methods within the mapper, by default it is `None` allowing obsolete source and target members to be mapped. This can be overriden by individual mapping methods using `MapperIgnoreObsoleteMembersAttribute`.
Sets the `IgnoreObsoleteMembersStrategy` for all methods within the mapper,
by default it is `None` allowing obsolete source and target members to be mapped.
This can be overriden by individual mapping methods using `MapperIgnoreObsoleteMembersAttribute`.

```csharp
// highlight-start
Expand All @@ -88,7 +92,8 @@ public partial class CarMapper
</TabItem>
<TabItem value="method" label="Local (mapping method level)">

Method will use the provided ignore obsolete mapping strategy, otherwise the `MapperAttribute` property `IgnoreObsoleteMembersStrategy` will be used.
Method will use the provided ignore obsolete mapping strategy,
otherwise the `MapperAttribute` property `IgnoreObsoleteMembersStrategy` will be used.

```csharp
[Mapper]
Expand Down
2 changes: 1 addition & 1 deletion src/Riok.Mapperly/Descriptors/DescriptorBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ SymbolAccessor symbolAccessor
);
}

public (MapperDescriptor descriptor, List<Diagnostic> diagnostics) Build()
public (MapperDescriptor descriptor, IReadOnlyCollection<Diagnostic> diagnostics) Build()
{
ReserveMethodNames();
ExtractObjectFactories();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,11 @@ private static void BuildInitOnlyMemberMappings(INewInstanceBuilderContext<IMapp
}

if (
!MemberPath.TryFind(
!ctx.BuilderContext.SymbolAccessor.TryFindMemberPath(
ctx.Mapping.SourceType,
MemberPathCandidateBuilder.BuildMemberPathCandidates(targetMember.Name),
ctx.IgnoredSourceMemberNames,
memberNameComparer,
ctx.BuilderContext.SymbolAccessor,
out var sourceMemberPath
)
)
Expand Down Expand Up @@ -105,12 +104,7 @@ IReadOnlyCollection<PropertyMappingConfiguration> memberConfigs
}

if (
!MemberPath.TryFind(
ctx.Mapping.SourceType,
memberConfig.Source.Path,
ctx.BuilderContext.SymbolAccessor,
out var sourceMemberPath
)
!ctx.BuilderContext.SymbolAccessor.TryFindMemberPath(ctx.Mapping.SourceType, memberConfig.Source.Path, out var sourceMemberPath)
)
{
ctx.BuilderContext.ReportDiagnostic(
Expand Down Expand Up @@ -302,12 +296,11 @@ private static bool TryFindConstructorParameterSourcePath(

if (!ctx.MemberConfigsByRootTargetName.TryGetValue(parameter.Name, out var memberConfigs))
{
return MemberPath.TryFind(
return ctx.BuilderContext.SymbolAccessor.TryFindMemberPath(
ctx.Mapping.SourceType,
MemberPathCandidateBuilder.BuildMemberPathCandidates(parameter.Name),
ctx.IgnoredSourceMemberNames,
StringComparer.OrdinalIgnoreCase,
ctx.BuilderContext.SymbolAccessor,
out sourcePath
);
}
Expand All @@ -332,7 +325,7 @@ out sourcePath
return false;
}

if (!MemberPath.TryFind(ctx.Mapping.SourceType, memberConfig.Source.Path, ctx.BuilderContext.SymbolAccessor, out sourcePath))
if (!ctx.BuilderContext.SymbolAccessor.TryFindMemberPath(ctx.Mapping.SourceType, memberConfig.Source.Path, out sourcePath))
{
ctx.BuilderContext.ReportDiagnostic(
DiagnosticDescriptors.SourceMemberNotFound,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,11 @@ public static void BuildMappingBody(IMembersContainerBuilderContext<IMemberAssig
}

if (
MemberPath.TryFind(
ctx.BuilderContext.SymbolAccessor.TryFindMemberPath(
ctx.Mapping.SourceType,
MemberPathCandidateBuilder.BuildMemberPathCandidates(targetMember.Name),
ctx.IgnoredSourceMemberNames,
memberNameComparer,
ctx.BuilderContext.SymbolAccessor,
out var sourceMemberPath
)
)
Expand Down Expand Up @@ -76,7 +75,7 @@ private static void BuildMemberAssignmentMapping(
PropertyMappingConfiguration config
)
{
if (!MemberPath.TryFind(ctx.Mapping.TargetType, config.Target.Path, ctx.BuilderContext.SymbolAccessor, out var targetMemberPath))
if (!ctx.BuilderContext.SymbolAccessor.TryFindMemberPath(ctx.Mapping.TargetType, config.Target.Path, out var targetMemberPath))
{
ctx.BuilderContext.ReportDiagnostic(
DiagnosticDescriptors.ConfiguredMappingTargetMemberNotFound,
Expand All @@ -86,7 +85,7 @@ PropertyMappingConfiguration config
return;
}

if (!MemberPath.TryFind(ctx.Mapping.SourceType, config.Source.Path, ctx.BuilderContext.SymbolAccessor, out var sourceMemberPath))
if (!ctx.BuilderContext.SymbolAccessor.TryFindMemberPath(ctx.Mapping.SourceType, config.Source.Path, out var sourceMemberPath))
{
ctx.BuilderContext.ReportDiagnostic(
DiagnosticDescriptors.ConfiguredMappingSourceMemberNotFound,
Expand Down
74 changes: 70 additions & 4 deletions src/Riok.Mapperly/Descriptors/SymbolAccessor.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
using Riok.Mapperly.Helpers;
using Riok.Mapperly.Symbols;
Expand Down Expand Up @@ -76,14 +77,79 @@ internal IReadOnlyCollection<IMappableMember> GetAllAccessibleMappableMembers(IT
return members;
}

internal IEnumerable<IMappableMember> GetMappableMembers(ITypeSymbol symbol, string name, IEqualityComparer<string> comparer)
internal bool TryFindMemberPath(
ITypeSymbol type,
IEnumerable<IEnumerable<string>> pathCandidates,
IReadOnlyCollection<string> ignoredNames,
IEqualityComparer<string> comparer,
[NotNullWhen(true)] out MemberPath? memberPath
)
{
foreach (var pathCandidate in FindMemberPathCandidates(type, pathCandidates, comparer))
{
if (ignoredNames.Contains(pathCandidate.Path.First().Name))
continue;

memberPath = pathCandidate;
return true;
}

memberPath = null;
return false;
}

internal bool TryFindMemberPath(ITypeSymbol type, IReadOnlyCollection<string> path, [NotNullWhen(true)] out MemberPath? memberPath) =>
TryFindMemberPath(type, path, StringComparer.Ordinal, out memberPath);

private IEnumerable<MemberPath> FindMemberPathCandidates(
ITypeSymbol type,
IEnumerable<IEnumerable<string>> pathCandidates,
IEqualityComparer<string> comparer
)
{
foreach (var pathCandidate in pathCandidates)
{
if (TryFindMemberPath(type, pathCandidate.ToList(), comparer, out var memberPath))
yield return memberPath;
}
}

private bool TryFindMemberPath(
ITypeSymbol type,
IReadOnlyCollection<string> path,
IEqualityComparer<string> comparer,
[NotNullWhen(true)] out MemberPath? memberPath
)
{
var foundPath = FindMemberPath(type, path, comparer).ToList();
if (foundPath.Count != path.Count)
{
memberPath = null;
return false;
}

memberPath = new(foundPath);
return true;
}

private IEnumerable<IMappableMember> FindMemberPath(ITypeSymbol type, IEnumerable<string> path, IEqualityComparer<string> comparer)
{
foreach (var name in path)
{
if (GetMappableMembers(type, name, comparer).FirstOrDefault() is not { } member)
break;

type = member.Type;
yield return member;
}
}

private IEnumerable<IMappableMember> GetMappableMembers(ITypeSymbol symbol, string name, IEqualityComparer<string> comparer)
{
foreach (var member in GetAllAccessibleMappableMembers(symbol))
{
if (comparer.Equals(name, member.Name))
{
if (comparer.Equals(member.Name, name))
yield return member;
}
}
}

Expand Down
7 changes: 4 additions & 3 deletions src/Riok.Mapperly/Descriptors/WellKnownTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,10 @@ public INamedTypeSymbol Get(Type type)
{
type = type.GetGenericTypeDefinition();
}

return Get(type.FullName ?? throw new InvalidOperationException("Could not get name of type " + type));
}

public INamedTypeSymbol Get(string typeFullName) =>
TryGet(typeFullName) ?? throw new InvalidOperationException("Could not get type " + typeFullName);

public INamedTypeSymbol? TryGet(string typeFullName)
{
if (_cachedTypes.TryGetValue(typeFullName, out var typeSymbol))
Expand All @@ -47,4 +45,7 @@ public INamedTypeSymbol Get(string typeFullName) =>

return typeSymbol;
}

private INamedTypeSymbol Get(string typeFullName) =>
TryGet(typeFullName) ?? throw new InvalidOperationException("Could not get type " + typeFullName);
}
12 changes: 5 additions & 7 deletions src/Riok.Mapperly/ImmutableEquatableArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public sealed class ImmutableEquatableArray<T> : IEquatable<ImmutableEquatableAr
public override int GetHashCode()
{
var hash = 0;
foreach (T value in _values)
foreach (var value in _values)
{
hash = HashHelper.Combine(hash, value.GetHashCode());
}
Expand Down Expand Up @@ -56,13 +56,11 @@ public bool MoveNext()
{
var newIndex = _index + 1;

if ((uint)newIndex < (uint)_values.Length)
{
_index = newIndex;
return true;
}
if ((uint)newIndex >= (uint)_values.Length)
return false;

return false;
_index = newIndex;
return true;
}

public readonly T Current => _values[_index];
Expand Down
Loading

0 comments on commit 32bfb80

Please sign in to comment.