Skip to content
Open
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
16 changes: 16 additions & 0 deletions src/ExpressionTranslator/ExpressionTranslator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public class ExpressionTranslator : ExpressionVisitor
private List<PropertyDefinitions>? _properties;
public List<PropertyDefinitions> Properties => _properties ??= new List<PropertyDefinitions>();

private HashSet<string>? _inheritDocMembers;

public bool HasDynamic { get; private set; }
public TypeDefinitions? Definitions { get; }

Expand All @@ -46,6 +48,18 @@ public ExpressionTranslator(TypeDefinitions? definitions = null)
ResetIndentLevel();
}

public void AddInheritDocMember(string memberName)
{
_inheritDocMembers ??= new HashSet<string>();
_inheritDocMembers.Add(memberName);
}

private void WriteInheritDocIfNeeded(string memberName)
{
if (_inheritDocMembers?.Contains(memberName) == true)
WriteNextLine("/// <inheritdoc />");
}

private void ResetIndentLevel()
{
_indentLevel = 0;
Expand Down Expand Up @@ -1204,6 +1218,7 @@ public Expression VisitLambda(LambdaExpression node, LambdaType type, string? me
if (!isInternal)
isInternal = node.ReturnType.GetTypeInfo().IsNotPublic ||
node.Parameters.Any(it => it.Type.GetTypeInfo().IsNotPublic);
WriteInheritDocIfNeeded(name);
WriteModifierNextLine(isInternal ? "internal" : "public");
var funcType = MakeDelegateType(node.ReturnType, node.Parameters.Select(it => it.Type).ToArray());
var exprType = typeof(Expression<>).MakeGenericType(funcType);
Expand Down Expand Up @@ -1238,6 +1253,7 @@ public Expression VisitLambda(LambdaExpression node, LambdaType type, string? me
if (!isInternal)
isInternal = node.ReturnType.GetTypeInfo().IsNotPublic ||
node.Parameters.Any(it => it.Type.GetTypeInfo().IsNotPublic);
WriteInheritDocIfNeeded(name);
WriteModifierNextLine(isInternal ? "internal" : "public");
Methods[name] = node.Type;
}
Expand Down
5 changes: 5 additions & 0 deletions src/Mapster.Tool.Tests/Mappers/IUserMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ namespace Mapster.Tool.Tests.Mappers;
[Mapper]
public interface IUserMapper
{
/// <summary>Gets the user projection expression.</summary>
Expression<Func<_User, _UserDto>> UserProjection { get; }

/// <summary>Maps a user to a DTO.</summary>
_UserDto MapTo(_User user);

/// <summary>Maps a user onto an existing DTO.</summary>
_UserDto MapTo(_User user, _UserDto userDto);
}
3 changes: 3 additions & 0 deletions src/Mapster.Tool.Tests/Mappers/UserMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ namespace Mapster.Tool.Tests.Mappers
{
public partial class UserMapper : IUserMapper
{
/// <inheritdoc />
public Expression<Func<_User, _UserDto>> UserProjection => p1 => new _UserDto()
{
Id = p1.Id,
Name = p1.Name
};
/// <inheritdoc />
public _UserDto MapTo(_User p2)
{
return p2 == null ? null : new _UserDto()
Expand All @@ -20,6 +22,7 @@ public _UserDto MapTo(_User p2)
Name = p2.Name
};
}
/// <inheritdoc />
public _UserDto MapTo(_User p3, _UserDto p4)
{
if (p3 == null)
Expand Down
1 change: 1 addition & 0 deletions src/Mapster.Tool.Tests/Mapster.Tool.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<Nullable>enable</Nullable>
<IsTestProject>true</IsTestProject>
<IsPackable>false</IsPackable>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<PropertyGroup Condition=" '$(OS)' == 'Windows_NT' ">
Expand Down
18 changes: 18 additions & 0 deletions src/Mapster.Tool.Tests/WhenGeneratingMapperWithDocumentation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using FluentAssertions;

namespace Mapster.Tool.Tests;

public class WhenGeneratingMapperWithDocumentation
{
[Fact]
public void GeneratedMapperShouldIncludeInheritDocForDocumentedMembers()
{
var generatedMapperPath = Path.GetFullPath(
Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "Mappers", "UserMapper.cs")
);

File.Exists(generatedMapperPath).Should().BeTrue();
var content = File.ReadAllText(generatedMapperPath);
content.Should().Contain("/// <inheritdoc />");
}
}
4 changes: 4 additions & 0 deletions src/Mapster.Tool/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ private static void GenerateMappers(MapperOptions opt)
var funcArgs = propArgs.GetGenericArguments();
var tuple = new TypeTuple(funcArgs[0], funcArgs[1]);
var expr = config.CreateMapExpression(tuple, MapType.Projection);
if (XmlDocumentationReader.HasDocumentation(assembly, prop))
translator.AddInheritDocMember(prop.Name);
translator.VisitLambda(
expr,
ExpressionTranslator.LambdaType.PublicLambda,
Expand All @@ -162,6 +164,8 @@ private static void GenerateMappers(MapperOptions opt)
tuple,
methodArgs.Length == 1 ? MapType.Map : MapType.MapToTarget
);
if (XmlDocumentationReader.HasDocumentation(assembly, method))
translator.AddInheritDocMember(method.Name);
translator.VisitLambda(
expr,
ExpressionTranslator.LambdaType.PublicMethod,
Expand Down
32 changes: 32 additions & 0 deletions src/Mapster.Tool/XmlDocumentationReader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Xml.Linq;

namespace Mapster.Tool
{
internal static class XmlDocumentationReader
{
public static bool HasDocumentation(Assembly assembly, MemberInfo member)
{
var assemblyPath = assembly.Location;
if (string.IsNullOrEmpty(assemblyPath))
return false;

var xmlPath = Path.ChangeExtension(assemblyPath, ".xml");
if (!File.Exists(xmlPath))
return false;

var memberId = DocumentationCommentId.CreateMemberId(member);
var element = XDocument.Load(xmlPath)
.Root?
.Element("members")?
.Elements("member")
.FirstOrDefault(it => (string?)it.Attribute("name") == memberId);

return element != null &&
element.Elements().Any(it => !string.IsNullOrWhiteSpace(it.Value));
}
}
}