1
0

chore: initial commit

Signed-off-by: Alan Brault <alan.brault@visus.io>
This commit is contained in:
2025-07-21 13:43:39 -04:00
commit 90d7bdcd21
37 changed files with 2647 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
namespace MapperSourceGen.SourceGenerator.Extensions;
using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
/// <summary>
/// Extension methods for <see cref="ITypeSymbol" />.
/// </summary>
internal static class TypeSymbolExtensions
{
/// <summary>
/// Converts an <see cref="ITypeSymbol" /> to a <see cref="TypeSyntax" />.
/// </summary>
/// <param name="source">The source <see cref="ITypeSymbol" /> instance to convert.</param>
/// <returns>An instance of <see cref="TypeSyntax" />.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TypeSyntax ToTypeSyntax(this ITypeSymbol source)
{
string typeName = source.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
return SyntaxFactory.ParseTypeName(typeName);
}
}

View File

@@ -0,0 +1,215 @@
namespace MapperSourceGen.SourceGenerator;
using System.Collections.Immutable;
using System.Text;
using Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Model;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
/// <summary>
/// Incremental source generator that is responsible for generating domain transfer objects (DTOs) and mapper from a
/// class decorated with the <c>MapperAttribute</c>.
/// </summary>
[Generator]
public sealed class MapperGenerator : IIncrementalGenerator
{
private const string MapIgnoreAttributeName = "MapperSourceGen.MapIgnoreAttribute";
private const string MapperAttributeName = "MapperSourceGen.MapperAttribute";
private const string Source = "source";
/// <inheritdoc />
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// This configures the generator pipeline to look for classes decorated with the MapperAttribute which will then extract
// the hierarchy information and properties to generate DTOs and mapping methods.
IncrementalValuesProvider<(HierarchyInfo Hierarchy, ImmutableArray<PropertyInfo> Properties)> items =
context.SyntaxProvider
.ForAttributeWithMetadataName(MapperAttributeName,
static (node, _) => node is ClassDeclarationSyntax { AttributeLists.Count: > 0 },
static (ctx, _) =>
{
if ( ctx.TargetSymbol is not INamedTypeSymbol classSymbol )
{
return default;
}
HierarchyInfo hierarchy = HierarchyInfo.From(classSymbol);
ImmutableArray<PropertyInfo> properties =
[
..classSymbol.GetMembers()
.OfType<IPropertySymbol>()
.Where(w => !w.IsIndexer
&& w is { IsAbstract: false, DeclaredAccessibility: Accessibility.Public }
&& !w.GetAttributes().Any(a => a.AttributeClass?.ToDisplayString() == MapIgnoreAttributeName))
.Select(s => new PropertyInfo(s))
];
return ( hierarchy, properties );
});
// This registers the source output for generating the domain transfer object (DTO).
context.RegisterSourceOutput(items,
static (ctx, item) =>
{
ImmutableArray<MemberDeclarationSyntax> properties = [..GenerateAutoProperties(item.Properties)];
CompilationUnitSyntax? compilationUnit = item.Hierarchy.GetCompilationUnit(properties, suffix: "Dto");
if ( compilationUnit is not null )
{
ctx.AddSource($"{item.Hierarchy.FileNameHint}Dto.g", compilationUnit.GetText(Encoding.UTF8));
}
});
// This registers the source output for generating the mapper class with mapping methods.
context.RegisterSourceOutput(items,
static (ctx, item) =>
{
ImmutableArray<MemberDeclarationSyntax> declarations =
[
GenerateMapFromMethod(item.Hierarchy, item.Properties),
GenerateMapToMethod(item.Hierarchy, item.Properties)
];
CompilationUnitSyntax? compilationUnit = item.Hierarchy.GetCompilationUnit(declarations, suffix: "Mapper");
if ( compilationUnit is not null )
{
ctx.AddSource($"{item.Hierarchy.FileNameHint}Mapper.g", compilationUnit.GetText(Encoding.UTF8));
}
});
}
/// <summary>
/// Generates an expression statement that validates the argument for null.
/// </summary>
/// <returns>
/// An expression statement that will output <code>ArgumentNullException.ThrowIfNull(nameof(source));</code>
/// </returns>
private static ExpressionStatementSyntax GenerateArgumentValidator()
{
MemberAccessExpressionSyntax member = MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
IdentifierName("ArgumentNullException"),
IdentifierName("ThrowIfNull"));
IdentifierNameSyntax nameofIdentifier = IdentifierName(Identifier(TriviaList(),
SyntaxKind.NameOfKeyword,
"nameof",
"nameof",
TriviaList()));
ArgumentSyntax argument = Argument(InvocationExpression(nameofIdentifier)
.WithArgumentList(ArgumentList(SingletonSeparatedList(Argument(IdentifierName(Source))))));
return ExpressionStatement(InvocationExpression(member)
.WithArgumentList(ArgumentList(SingletonSeparatedList(argument))));
}
/// <summary>
/// Generates auto properties used in the domain transfer object (DTO).
/// </summary>
/// <param name="properties">An <see cref="IEnumerable{T}" /> collection of properties to generate auto-properties for.</param>
/// <returns>
/// An <see cref="IEnumerable{T}" /> of <see cref="MemberDeclarationSyntax" /> items that will output auto-properties:
/// <code>
/// public string Name { get; set;
/// </code>
/// </returns>
private static IEnumerable<MemberDeclarationSyntax> GenerateAutoProperties(IEnumerable<PropertyInfo> properties)
{
return properties.Select(property => PropertyDeclaration(property.PropertyType.ToTypeSyntax(), property.TargetPropertyName)
.AddModifiers(Token(SyntaxKind.PublicKeyword))
.AddAccessorListAccessors(
AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
));
}
/// <summary>
/// Generates a method that maps a DTO to a model.
/// </summary>
/// <param name="hierarchy">An <see cref="HierarchyInfo" /> instance that contains information about the model.</param>
/// <param name="properties">An <see cref="IEnumerable{T}" /> of properties to be used in mapping the DTO to the model.</param>
/// <returns>
/// An instance of <see cref="MemberDeclarationSyntax" /> containing the method that returns an instance of the
/// model.
/// </returns>
/// <remarks>Properties that are decorated with the <c>MapIgnoreAttribute</c> will not be included in the mapping.</remarks>
private static MethodDeclarationSyntax GenerateMapFromMethod(HierarchyInfo hierarchy, ImmutableArray<PropertyInfo> properties)
{
ParameterSyntax parameter = Parameter(Identifier(Source))
.WithType(ParseTypeName($"{hierarchy.FileNameHint}Dto"));
ReturnStatementSyntax returnStatement = ReturnStatement(GenerateMappingMethod(hierarchy, properties, isSource: false));
return MethodDeclaration(ParseTypeName($"{hierarchy.FileNameHint}"), "ToModel")
.AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))
.AddParameterListParameters(parameter)
.WithBody(Block(GenerateArgumentValidator(), returnStatement));
}
private static ObjectCreationExpressionSyntax GenerateMappingMethod(HierarchyInfo hierarchy,
ImmutableArray<PropertyInfo> properties,
string? suffix = null,
bool isSource = true)
{
return ObjectCreationExpression(IdentifierName(ParseTypeName($"{hierarchy.FileNameHint}{suffix}").ToString()))
.WithArgumentList(ArgumentList())
.WithInitializer(InitializerExpression(SyntaxKind.ObjectInitializerExpression,
SeparatedList<ExpressionSyntax>(GeneratePropertyAssignments(properties, isSource))));
}
/// <summary>
/// Generates a method that maps a model to a DTO.
/// </summary>
/// <param name="hierarchy">An <see cref="HierarchyInfo" /> instance that contains information about the model.</param>
/// <param name="properties">An <see cref="IEnumerable{T}" /> of properties to be used in mapping the model to the DTO.</param>
/// <returns>
/// An instance of <see cref="MemberDeclarationSyntax" /> containing the method that returns an instance of the
/// DTO.
/// </returns>
/// <remarks>Properties that are decorated with the <c>MapIgnoreAttribute</c> will not be included in the mapping.</remarks>
private static MethodDeclarationSyntax GenerateMapToMethod(HierarchyInfo hierarchy, ImmutableArray<PropertyInfo> properties)
{
ParameterSyntax parameter = Parameter(Identifier(Source))
.WithType(ParseTypeName(hierarchy.FileNameHint));
ReturnStatementSyntax returnStatement = ReturnStatement(GenerateMappingMethod(hierarchy, properties, "Dto"));
return MethodDeclaration(ParseTypeName($"{hierarchy.FileNameHint}Dto"), "ToDto")
.AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))
.AddParameterListParameters(parameter)
.WithBody(Block(GenerateArgumentValidator(), returnStatement));
}
/// <summary>
/// Generates property assignments for the object initializer in the mapping methods.
/// </summary>
/// <param name="properties">An <see cref="IEnumerable{T}" /> of properties that are to be included in the initializer.</param>
/// <param name="isSource">
/// Determines whether the <c>MapAliasAttribute</c> is to be used on the left or right side of
/// assignment.
/// </param>
/// <returns>
/// An <see cref="IEnumerable{T}" /> containing <see cref="SyntaxNodeOrToken" /> instances for property
/// assignment.
/// </returns>
private static IEnumerable<SyntaxNodeOrToken> GeneratePropertyAssignments(IEnumerable<PropertyInfo> properties, bool isSource = true)
{
return properties.SelectMany(s => new SyntaxNodeOrToken[]
{
AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
IdentifierName(!isSource ? s.SourcePropertyName : s.TargetPropertyName),
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
IdentifierName(Source),
IdentifierName(isSource ? s.SourcePropertyName : s.TargetPropertyName))),
Token(SyntaxKind.CommaToken)
});
}
}

View File

@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!--
Source generator projects must target .NET Standard 2.0 as the Roslyn SDK Compiler only supports this version
for compiler addons.
-->
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<!-- AnalyzerLanguage is set to cs to denote that only C# is supported by the source generator. -->
<AnalyzerLanguage>cs</AnalyzerLanguage>
<!-- EmitCompilerGeneratedFiles is set to true to ensure that the source generator can emit artifacts. -->
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<!-- EnforceExtendedAnalyzerRules is set to true to enable additional rules for analyzers. -->
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<!-- IsRoslynComponent is set to true to indicate that this project is a Roslyn component. -->
<IsRoslynComponent>true</IsRoslynComponent>
</PropertyGroup>
<ItemGroup>
<!-- Microsoft.CodeAnalysis.CSharp is required for C# source generators. -->
<PackageReference Include="Microsoft.CodeAnalysis.CSharp"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,71 @@
// This file is ported and adapted from CommunityToolkit.Mvvm (CommunityToolkit/dotnet)
namespace MapperSourceGen.SourceGenerator.Model;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
internal partial class HierarchyInfo
{
/// <summary>
/// Gets a <see cref="CompilationUnitSyntax" /> representing the type hierarchy with the specified member declarations.
/// </summary>
/// <param name="memberDeclarations">
/// The <see cref="MemberDeclarationSyntax" /> instances to include in the
/// <see cref="CompilationUnitSyntax" />.
/// </param>
/// <param name="prefix">The prefix to prepend to the file and class name.</param>
/// <param name="suffix">The suffix to append to the file and class name.</param>
/// <returns>An instance of <see cref="CompilationUnitSyntax" /> used for generating the final output.</returns>
public CompilationUnitSyntax GetCompilationUnit(ImmutableArray<MemberDeclarationSyntax> memberDeclarations, string? prefix = null, string? suffix = null)
{
TypeDeclarationSyntax typeDeclarationSyntax =
Hierarchy[0].GetSyntax(prefix, suffix)
.AddModifiers(Token(TriviaList(Comment("/// <inheritdoc/>")),
Hierarchy[0].AccessibilityKind,
TriviaList()))
.AddModifiers(GetKeywordModifierTokens(Hierarchy[0]))
.AddMembers([.. memberDeclarations])
.NormalizeWhitespace();
foreach ( TypeInfo parentType in Hierarchy.AsSpan().Slice(1) )
{
typeDeclarationSyntax =
parentType.GetSyntax(prefix, suffix)
.AddModifiers(Token(TriviaList(Comment("/// <inheritdoc/>")),
parentType.AccessibilityKind,
TriviaList()))
.AddModifiers(GetKeywordModifierTokens(parentType))
.AddMembers(typeDeclarationSyntax)
.NormalizeWhitespace();
}
SyntaxTriviaList syntaxTriviaList = TriviaList(
Comment("// <auto-generated/>"),
Trivia(PragmaWarningDirectiveTrivia(Token(SyntaxKind.DisableKeyword), true)),
Trivia(NullableDirectiveTrivia(Token(SyntaxKind.EnableKeyword), true)));
return CompilationUnit()
.AddMembers(NamespaceDeclaration(IdentifierName(Namespace))
.WithLeadingTrivia(syntaxTriviaList)
.AddMembers(typeDeclarationSyntax))
.NormalizeWhitespace();
}
private static SyntaxToken[] GetKeywordModifierTokens(TypeInfo typeInfo)
{
HashSet<SyntaxToken> tokens = [];
if ( typeInfo.IsSealed )
{
tokens.Add(Token(SyntaxKind.SealedKeyword));
}
tokens.Add(Token(SyntaxKind.PartialKeyword));
return [.. tokens];
}
}

View File

@@ -0,0 +1,115 @@
// This file is ported and adapted from CommunityToolkit.Mvvm (CommunityToolkit/dotnet)
namespace MapperSourceGen.SourceGenerator.Model;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using static Microsoft.CodeAnalysis.SymbolDisplayTypeQualificationStyle;
/// <summary>
/// Represents information about a type hierarchy in a source code file.
/// </summary>
internal sealed partial class HierarchyInfo : IEquatable<HierarchyInfo>
{
/// <summary>
/// Initializes a new instance of the <see cref="HierarchyInfo" /> class.
/// </summary>
/// <param name="fileNameHint">The filename hint for the type (including namespace) without extension.</param>
/// <param name="namespace">The containing namespace for the type.</param>
/// <param name="hierarchy">The current hierarchy for the type.</param>
/// <exception cref="ArgumentException"><paramref name="fileNameHint" /> is <c>null</c> or empty.</exception>
private HierarchyInfo(string fileNameHint, string @namespace, ImmutableArray<TypeInfo> hierarchy)
{
if ( string.IsNullOrWhiteSpace(fileNameHint) )
{
throw new ArgumentException($"'{nameof(fileNameHint)}' cannot be null or empty.", nameof(fileNameHint));
}
FileNameHint = fileNameHint;
Hierarchy = hierarchy;
Namespace = @namespace;
}
/// <summary>
/// Gets the file name hint (including full namespace) for the type hierarchy.
/// </summary>
public string FileNameHint { get; }
/// <summary>
/// Gets a collection of <see cref="TypeInfo" /> representing the hierarchy of types.
/// </summary>
public ImmutableArray<TypeInfo> Hierarchy { get; }
/// <summary>
/// Gets the namespace of the type hierarchy.
/// </summary>
public string Namespace { get; }
public static HierarchyInfo From(INamedTypeSymbol typeSymbol)
{
if ( typeSymbol is null )
{
throw new ArgumentNullException(nameof(typeSymbol));
}
LinkedList<TypeInfo> hierarchy = [];
for ( INamedTypeSymbol? parent = typeSymbol;
parent is not null;
parent = parent.ContainingType )
{
hierarchy.AddLast(new TypeInfo(parent.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat),
parent.TypeKind,
parent.DeclaredAccessibility,
parent.IsRecord,
parent.IsSealed));
}
return new HierarchyInfo(typeSymbol.ToDisplayString(new SymbolDisplayFormat(typeQualificationStyle: NameAndContainingTypesAndNamespaces)),
typeSymbol.ContainingNamespace.ToDisplayString(new SymbolDisplayFormat(typeQualificationStyle: NameAndContainingTypesAndNamespaces)),
[..hierarchy]);
}
public static bool operator ==(HierarchyInfo? left, HierarchyInfo? right)
{
return Equals(left, right);
}
public static bool operator !=(HierarchyInfo? left, HierarchyInfo? right)
{
return !Equals(left, right);
}
/// <inheritdoc />
public bool Equals(HierarchyInfo? other)
{
if ( other is null )
{
return false;
}
if ( ReferenceEquals(this, other) )
{
return true;
}
return string.Equals(FileNameHint, other.FileNameHint, StringComparison.OrdinalIgnoreCase)
&& string.Equals(Namespace, other.Namespace, StringComparison.OrdinalIgnoreCase);
}
/// <inheritdoc />
public override bool Equals(object? obj)
{
return ReferenceEquals(this, obj) || ( obj is HierarchyInfo other && Equals(other) );
}
/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
return ( StringComparer.OrdinalIgnoreCase.GetHashCode(FileNameHint) * 397 )
^ StringComparer.OrdinalIgnoreCase.GetHashCode(Namespace);
}
}
}

View File

@@ -0,0 +1,105 @@
namespace MapperSourceGen.SourceGenerator.Model;
using Microsoft.CodeAnalysis;
/// <summary>
/// Represents information about a property in a source code file.
/// </summary>
internal readonly struct PropertyInfo : IEquatable<PropertyInfo>
{
private const string MapAliasAttributeName = "MapperSourceGen.MapAliasAttribute";
/// <summary>
/// Initializes a new instance of the <see cref="PropertyInfo" /> struct.
/// </summary>
/// <param name="propertySymbol">instance of <see cref="IPropertySymbol" />.</param>
/// <exception cref="ArgumentNullException"><paramref name="propertySymbol" /> is <c>null</c>.</exception>
public PropertyInfo(IPropertySymbol propertySymbol)
{
if ( propertySymbol is null )
{
throw new ArgumentNullException(nameof(propertySymbol));
}
SourcePropertyName = TargetPropertyName = propertySymbol.Name;
if ( !propertySymbol.GetAttributes().IsEmpty )
{
AttributeData? attribute = propertySymbol
.GetAttributes()
.FirstOrDefault(f => f.AttributeClass?.ToDisplayString() == MapAliasAttributeName);
if ( attribute is not null )
{
TargetPropertyName = attribute.ConstructorArguments[0].Value!.ToString();
}
}
PropertyType = propertySymbol.Type;
}
/// <summary>
/// Gets the type of the property.
/// </summary>
public ITypeSymbol PropertyType { get; }
/// <summary>
/// Gets the name of the source property.
/// </summary>
public string SourcePropertyName { get; }
/// <summary>
/// Gets the name of the target property (if different from the source).
/// </summary>
public string TargetPropertyName { get; }
/// <summary>
/// Indicates whether two <see cref="PropertyInfo" /> instances are equal based on their properties.
/// </summary>
/// <param name="left">the first instance to compare</param>
/// <param name="right">the second instance to compare</param>
/// <returns><c>true</c> if both instances are equal, otherwise <c>false</c>.</returns>
public static bool operator ==(PropertyInfo left, PropertyInfo right)
{
return left.Equals(right);
}
/// <summary>
/// Indicates whether two <see cref="PropertyInfo" /> instances are not equal based on their properties.
/// </summary>
/// <param name="left">the first instance to compare</param>
/// <param name="right">the second instance to compare</param>
/// <returns><c>true</c> if both instances are not equal, otherwise <c>false</c>.</returns>
public static bool operator !=(PropertyInfo left, PropertyInfo right)
{
return !left.Equals(right);
}
/// <inheritdoc />
public bool Equals(PropertyInfo other)
{
return SymbolEqualityComparer.Default.Equals(PropertyType, other.PropertyType)
&& string.Equals(SourcePropertyName, other.SourcePropertyName, StringComparison.OrdinalIgnoreCase)
&& string.Equals(TargetPropertyName, other.TargetPropertyName, StringComparison.OrdinalIgnoreCase);
}
/// <inheritdoc />
public override bool Equals(object? obj)
{
return obj is PropertyInfo other && Equals(other);
}
/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
int hashCode = SymbolEqualityComparer.Default.GetHashCode(PropertyType);
hashCode = ( hashCode * 397 ) ^ StringComparer.OrdinalIgnoreCase.GetHashCode(SourcePropertyName);
hashCode = ( hashCode * 397 ) ^ StringComparer.OrdinalIgnoreCase.GetHashCode(TargetPropertyName);
return hashCode;
}
}
}

View File

@@ -0,0 +1,48 @@
// This file is ported and adapted from CommunityToolkit.Mvvm (CommunityToolkit/dotnet)
namespace MapperSourceGen.SourceGenerator.Model;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
internal sealed record TypeInfo(
string QualifiedName,
TypeKind Kind,
Accessibility DeclaredAccessibility,
bool IsRecord,
bool IsSealed)
{
public SyntaxKind AccessibilityKind =>
DeclaredAccessibility switch
{
Accessibility.Public => SyntaxKind.PublicKeyword,
Accessibility.Internal => SyntaxKind.InternalKeyword,
Accessibility.Private => SyntaxKind.PrivateKeyword,
Accessibility.Protected => SyntaxKind.ProtectedKeyword,
_ => SyntaxKind.None
};
public Accessibility DeclaredAccessibility { get; } = DeclaredAccessibility;
public bool IsRecord { get; } = IsRecord;
public bool IsSealed { get; } = IsSealed;
public TypeKind Kind { get; } = Kind;
public string QualifiedName { get; } = QualifiedName;
public TypeDeclarationSyntax GetSyntax(string? prefix = null, string? suffix = null)
{
return Kind switch
{
TypeKind.Class when IsRecord =>
RecordDeclaration(Token(SyntaxKind.RecordKeyword), $"{prefix}{QualifiedName}{suffix}")
.WithOpenBraceToken(Token(SyntaxKind.OpenBraceToken))
.WithCloseBraceToken(Token(SyntaxKind.CloseBraceToken)),
_ => ClassDeclaration($"{prefix}{QualifiedName}{suffix}")
};
}
}

View File

@@ -0,0 +1,9 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"SourceGenerator": {
"commandName": "DebugRoslynComponent",
"targetProject": "../../sample/MapperSourceGen.Sample/MapperSourceGen.Sample.csproj"
}
}
}

View File

@@ -0,0 +1,121 @@
{
"version": 2,
"dependencies": {
".NETStandard,Version=v2.0": {
"Microsoft.CodeAnalysis.CSharp": {
"type": "Direct",
"requested": "[4.11.0, 4.11.0]",
"resolved": "4.11.0",
"contentHash": "6XYi2EusI8JT4y2l/F3VVVS+ISoIX9nqHsZRaG6W5aFeJ5BEuBosHfT/ABb73FN0RZ1Z3cj2j7cL28SToJPXOw==",
"dependencies": {
"Microsoft.CodeAnalysis.Analyzers": "3.3.4",
"Microsoft.CodeAnalysis.Common": "[4.11.0]",
"System.Buffers": "4.5.1",
"System.Collections.Immutable": "8.0.0",
"System.Memory": "4.5.5",
"System.Numerics.Vectors": "4.5.0",
"System.Reflection.Metadata": "8.0.0",
"System.Runtime.CompilerServices.Unsafe": "6.0.0",
"System.Text.Encoding.CodePages": "7.0.0",
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"NETStandard.Library": {
"type": "Direct",
"requested": "[2.0.3, )",
"resolved": "2.0.3",
"contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0"
}
},
"Microsoft.CodeAnalysis.Analyzers": {
"type": "Transitive",
"resolved": "3.3.4",
"contentHash": "AxkxcPR+rheX0SmvpLVIGLhOUXAKG56a64kV9VQZ4y9gR9ZmPXnqZvHJnmwLSwzrEP6junUF11vuc+aqo5r68g=="
},
"Microsoft.NETCore.Platforms": {
"type": "Transitive",
"resolved": "1.1.0",
"contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
},
"System.Buffers": {
"type": "Transitive",
"resolved": "4.5.1",
"contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
},
"System.Collections.Immutable": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==",
"dependencies": {
"System.Memory": "4.5.5",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Memory": {
"type": "Transitive",
"resolved": "4.5.5",
"contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
"dependencies": {
"System.Buffers": "4.5.1",
"System.Numerics.Vectors": "4.4.0",
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
}
},
"System.Numerics.Vectors": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
},
"System.Reflection.Metadata": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "ptvgrFh7PvWI8bcVqG5rsA/weWM09EnthFHR5SCnS6IN+P4mj6rE1lBDC4U8HL9/57htKAqy4KQ3bBj84cfYyQ==",
"dependencies": {
"System.Collections.Immutable": "8.0.0",
"System.Memory": "4.5.5"
}
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
},
"System.Text.Encoding.CodePages": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "LSyCblMpvOe0N3E+8e0skHcrIhgV2huaNcjUUEa8hRtgEAm36aGkRoC8Jxlb6Ra6GSfF29ftduPNywin8XolzQ==",
"dependencies": {
"System.Memory": "4.5.5",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Threading.Tasks.Extensions": {
"type": "Transitive",
"resolved": "4.5.4",
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
}
},
"Microsoft.CodeAnalysis.Common": {
"type": "CentralTransitive",
"requested": "[4.11.0, )",
"resolved": "4.11.0",
"contentHash": "djf8ujmqYImFgB04UGtcsEhHrzVqzHowS+EEl/Yunc5LdrYrZhGBWUTXoCF0NzYXJxtfuD+UVQarWpvrNc94Qg==",
"dependencies": {
"Microsoft.CodeAnalysis.Analyzers": "3.3.4",
"System.Buffers": "4.5.1",
"System.Collections.Immutable": "8.0.0",
"System.Memory": "4.5.5",
"System.Numerics.Vectors": "4.5.0",
"System.Reflection.Metadata": "8.0.0",
"System.Runtime.CompilerServices.Unsafe": "6.0.0",
"System.Text.Encoding.CodePages": "7.0.0",
"System.Threading.Tasks.Extensions": "4.5.4"
}
}
}
}
}

View File

@@ -0,0 +1,27 @@
namespace MapperSourceGen;
/// <summary>
/// Specifies that the target property should appear in the domain transfer object with a different name.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public sealed class MapAliasAttribute : Attribute
{
private MapAliasAttribute()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="MapAliasAttribute" /> class with the specified name.
/// </summary>
/// <param name="name">The name to use instead of the property name.</param>
public MapAliasAttribute(string name)
{
ArgumentException.ThrowIfNullOrWhiteSpace(name);
Name = name;
}
/// <summary>
/// The name of the property in the domain transfer object.
/// </summary>
public string? Name { get; }
}

View File

@@ -0,0 +1,9 @@
namespace MapperSourceGen;
/// <summary>
/// Specifies that the target property should not be included in the domain transfer object or mapper.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public sealed class MapIgnoreAttribute : Attribute
{
}

View File

@@ -0,0 +1,9 @@
namespace MapperSourceGen;
/// <summary>
/// Specifies that the target class is a candidate for DTO and mapper generation.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public sealed class MapperAttribute : Attribute
{
}

View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<!--
When referencing a source generator that is to part of the nuget package as an analyzer, ensure that the following properties are set:
- ReferenceOutputAssembly="false" to prevent the source generator from being treated as a regular assembly.
- PackAsAnalyzer="true" to include the source generator as an analyzer in the NuGet package.
-->
<ProjectReference Include="..\MapperSourceGen.SourceGenerator\MapperSourceGen.SourceGenerator.csproj"
ReferenceOutputAssembly="false"
PackAsAnalyzer="true"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,6 @@
{
"version": 2,
"dependencies": {
"net8.0": {}
}
}