Cleanup.
This commit is contained in:
parent
3ff1e90fb9
commit
2a671a9dd4
|
@ -0,0 +1,72 @@
|
||||||
|
|
||||||
|
[*.{appxmanifest,asax,ascx,aspx,axaml,build,cg,cginc,compute,cs,cshtml,dtd,fs,fsi,fsscript,fsx,hlsl,hlsli,hlslinc,master,ml,mli,nuspec,paml,razor,resw,resx,skin,usf,ush,vb,xaml,xamlx,xoml,xsd}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
tab_width = 4
|
||||||
|
|
||||||
|
[*]
|
||||||
|
|
||||||
|
# Microsoft .NET properties
|
||||||
|
csharp_new_line_before_members_in_object_initializers = false
|
||||||
|
csharp_preferred_modifier_order = public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
|
||||||
|
csharp_space_after_cast = false
|
||||||
|
csharp_style_var_elsewhere = true:suggestion
|
||||||
|
csharp_style_var_for_built_in_types = true:suggestion
|
||||||
|
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||||
|
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
|
||||||
|
dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:none
|
||||||
|
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none
|
||||||
|
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
|
||||||
|
dotnet_style_predefined_type_for_member_access = true:suggestion
|
||||||
|
dotnet_style_qualification_for_event = false:suggestion
|
||||||
|
dotnet_style_qualification_for_field = false:suggestion
|
||||||
|
dotnet_style_qualification_for_method = false:suggestion
|
||||||
|
dotnet_style_qualification_for_property = false:suggestion
|
||||||
|
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
|
||||||
|
|
||||||
|
# ReSharper properties
|
||||||
|
resharper_autodetect_indent_settings = true
|
||||||
|
resharper_blank_lines_after_control_transfer_statements = 1
|
||||||
|
resharper_blank_lines_after_multiline_statements = 1
|
||||||
|
resharper_blank_lines_around_block_case_section = 1
|
||||||
|
resharper_blank_lines_around_multiline_case_section = 1
|
||||||
|
resharper_blank_lines_around_single_line_auto_property = 1
|
||||||
|
resharper_blank_lines_around_single_line_local_method = 1
|
||||||
|
resharper_blank_lines_around_single_line_property = 1
|
||||||
|
resharper_braces_for_for = required
|
||||||
|
resharper_braces_for_foreach = required
|
||||||
|
resharper_braces_for_ifelse = required
|
||||||
|
resharper_braces_for_while = required
|
||||||
|
resharper_csharp_blank_lines_around_single_line_invocable = 1
|
||||||
|
resharper_csharp_empty_block_style = together_same_line
|
||||||
|
resharper_csharp_keep_blank_lines_in_code = 1
|
||||||
|
resharper_csharp_keep_blank_lines_in_declarations = 1
|
||||||
|
resharper_csharp_max_line_length = 180
|
||||||
|
resharper_csharp_wrap_lines = false
|
||||||
|
resharper_local_function_body = expression_body
|
||||||
|
resharper_method_or_operator_body = expression_body
|
||||||
|
resharper_place_accessorholder_attribute_on_same_line = false
|
||||||
|
resharper_place_field_attribute_on_same_line = false
|
||||||
|
resharper_space_after_cast = false
|
||||||
|
resharper_space_within_single_line_array_initializer_braces = true
|
||||||
|
resharper_use_indent_from_vs = false
|
||||||
|
resharper_xmldoc_indent_text = ZeroIndent
|
||||||
|
|
||||||
|
# ReSharper inspection severities
|
||||||
|
resharper_arguments_style_literal_highlighting = none
|
||||||
|
resharper_arguments_style_named_expression_highlighting = none
|
||||||
|
resharper_arguments_style_other_highlighting = none
|
||||||
|
resharper_arrange_redundant_parentheses_highlighting = hint
|
||||||
|
resharper_arrange_this_qualifier_highlighting = hint
|
||||||
|
resharper_arrange_type_member_modifiers_highlighting = hint
|
||||||
|
resharper_arrange_type_modifiers_highlighting = hint
|
||||||
|
resharper_built_in_type_reference_style_for_member_access_highlighting = hint
|
||||||
|
resharper_built_in_type_reference_style_highlighting = hint
|
||||||
|
resharper_class_never_instantiated_global_highlighting = none
|
||||||
|
resharper_redundant_base_qualifier_highlighting = warning
|
||||||
|
resharper_suggest_var_or_type_built_in_types_highlighting = hint
|
||||||
|
resharper_suggest_var_or_type_elsewhere_highlighting = hint
|
||||||
|
resharper_suggest_var_or_type_simple_types_highlighting = hint
|
||||||
|
resharper_web_config_module_not_resolved_highlighting = warning
|
||||||
|
resharper_web_config_type_not_resolved_highlighting = warning
|
||||||
|
resharper_web_config_wrong_module_highlighting = warning
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Mohammadreza Taikandi
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -12,7 +12,5 @@ namespace System.Runtime.CompilerServices
|
||||||
/// This class should not be used by developers in source code.
|
/// This class should not be used by developers in source code.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||||
internal static class IsExternalInit
|
internal static class IsExternalInit { }
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -6,16 +6,16 @@ namespace MapTo
|
||||||
{
|
{
|
||||||
private const string UsageCategory = "Usage";
|
private const string UsageCategory = "Usage";
|
||||||
|
|
||||||
internal static Diagnostic SymbolNotFound(Location location, string syntaxName) =>
|
internal static Diagnostic SymbolNotFound(Location location, string syntaxName) =>
|
||||||
Diagnostic.Create(CreateDescriptor("MT0001", "Symbol not found.", $"Unable to find any symbols for {syntaxName}"), location);
|
Create("MT0001", "Symbol not found.", $"Unable to find any symbols for {syntaxName}", location);
|
||||||
|
|
||||||
internal static Diagnostic MapFromAttributeNotFound(Location location) =>
|
internal static Diagnostic MapFromAttributeNotFound(Location location) =>
|
||||||
Diagnostic.Create(CreateDescriptor("MT0002", "Attribute Not Available", $"Unable to find {SourceBuilder.MapFromAttributeName} type."), location);
|
Create("MT0002", "Attribute Not Available", $"Unable to find {SourceBuilder.MapFromAttributeName} type.", location);
|
||||||
|
|
||||||
internal static Diagnostic ClassMappingsGenerated(Location location, string typeName) =>
|
internal static Diagnostic ClassMappingsGenerated(Location location, string typeName) =>
|
||||||
Diagnostic.Create(CreateDescriptor("MT1001", "Mapped Type", $"Generated mappings for {typeName}", DiagnosticSeverity.Info), location);
|
Create("MT1001", "Mapped Type", $"Generated mappings for {typeName}", location, DiagnosticSeverity.Info);
|
||||||
|
|
||||||
private static DiagnosticDescriptor CreateDescriptor(string id, string title, string message, DiagnosticSeverity severity = DiagnosticSeverity.Error) =>
|
private static Diagnostic Create(string id, string title, string message, Location location, DiagnosticSeverity severity = DiagnosticSeverity.Error) =>
|
||||||
new(id, title, message, UsageCategory, severity, true);
|
Diagnostic.Create(new DiagnosticDescriptor(id, title, message, UsageCategory, severity, true), location);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -23,35 +23,11 @@ namespace MapTo.Extensions
|
||||||
return type.GetBaseTypesAndThis().SelectMany(n => n.GetMembers());
|
return type.GetBaseTypesAndThis().SelectMany(n => n.GetMembers());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<T> GetAllMembersOfType<T>(this ITypeSymbol type) where T : ISymbol
|
public static IEnumerable<T> GetAllMembersOfType<T>(this ITypeSymbol type) where T : ISymbol => type.GetAllMembers().OfType<T>();
|
||||||
{
|
|
||||||
return type.GetAllMembers().OfType<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CompilationUnitSyntax GetCompilationUnit(this SyntaxNode syntaxNode)
|
public static CompilationUnitSyntax GetCompilationUnit(this SyntaxNode syntaxNode) => syntaxNode.Ancestors().OfType<CompilationUnitSyntax>().Single();
|
||||||
{
|
|
||||||
return syntaxNode.Ancestors().OfType<CompilationUnitSyntax>().Single();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetClassName(this ClassDeclarationSyntax classSyntax)
|
public static string GetClassName(this ClassDeclarationSyntax classSyntax) => classSyntax.Identifier.Text;
|
||||||
{
|
|
||||||
return classSyntax.Identifier.Text;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetClassModifier(this ClassDeclarationSyntax classSyntax)
|
|
||||||
{
|
|
||||||
return classSyntax.Modifiers.ToFullString().Trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool HaveAttribute(this ClassDeclarationSyntax classSyntax, string attributeName)
|
|
||||||
{
|
|
||||||
return classSyntax.AttributeLists.Count > 0 &&
|
|
||||||
classSyntax.AttributeLists.SelectMany(al => al.Attributes
|
|
||||||
.Where(a =>
|
|
||||||
(a.Name as IdentifierNameSyntax)?.Identifier.Text == attributeName ||
|
|
||||||
((a.Name as QualifiedNameSyntax)?.Right as IdentifierNameSyntax)?.Identifier.ValueText == attributeName))
|
|
||||||
.Any();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AttributeSyntax? GetAttribute(this ClassDeclarationSyntax classSyntax, string attributeName)
|
public static AttributeSyntax? GetAttribute(this ClassDeclarationSyntax classSyntax, string attributeName)
|
||||||
{
|
{
|
||||||
|
@ -62,21 +38,11 @@ namespace MapTo.Extensions
|
||||||
((a.Name as QualifiedNameSyntax)?.Right as IdentifierNameSyntax)?.Identifier.ValueText == attributeName);
|
((a.Name as QualifiedNameSyntax)?.Right as IdentifierNameSyntax)?.Identifier.ValueText == attributeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string? GetNamespace(this CompilationUnitSyntax root)
|
public static string? GetNamespace(this CompilationUnitSyntax root) =>
|
||||||
{
|
root.ChildNodes()
|
||||||
return root.ChildNodes()
|
|
||||||
.OfType<NamespaceDeclarationSyntax>()
|
.OfType<NamespaceDeclarationSyntax>()
|
||||||
.FirstOrDefault()
|
.FirstOrDefault()
|
||||||
?.Name
|
?.Name
|
||||||
.ToString();
|
.ToString();
|
||||||
}
|
|
||||||
|
|
||||||
public static List<string> GetUsings(this CompilationUnitSyntax root)
|
|
||||||
{
|
|
||||||
return root.ChildNodes()
|
|
||||||
.OfType<UsingDirectiveSyntax>()
|
|
||||||
.Select(n => n.Name.ToString())
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,10 +15,7 @@ namespace MapTo.Extensions
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static StringBuilder AppendOpeningBracket(this StringBuilder builder, int indent = 0)
|
internal static StringBuilder AppendOpeningBracket(this StringBuilder builder, int indent = 0) => builder.AppendLine().PadLeft(indent).AppendFormat("{{{0}", Environment.NewLine);
|
||||||
{
|
|
||||||
return builder.AppendLine().PadLeft(indent).AppendFormat("{{{0}", Environment.NewLine);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static StringBuilder AppendClosingBracket(this StringBuilder builder, int indent = 0, bool padNewLine = true)
|
internal static StringBuilder AppendClosingBracket(this StringBuilder builder, int indent = 0, bool padNewLine = true)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
{
|
{
|
||||||
internal static class StringExtensions
|
internal static class StringExtensions
|
||||||
{
|
{
|
||||||
public static string ToCamelCase(this string value)
|
public static string ToCamelCase(this string value) => string.IsNullOrWhiteSpace(value) ? value : $"{char.ToLower(value[0])}{value.Substring(1)}";
|
||||||
{
|
|
||||||
return string.IsNullOrWhiteSpace(value) ? value : $"{char.ToLower(value[0])}{value.Substring(1)}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using MapTo.Extensions;
|
|
||||||
using MapTo.Models;
|
using MapTo.Models;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
@ -19,57 +18,36 @@ namespace MapTo
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Execute(GeneratorExecutionContext context)
|
public void Execute(GeneratorExecutionContext context)
|
||||||
{
|
{
|
||||||
context.AddMapToAttribute();
|
AddMapFromAttribute(context);
|
||||||
|
|
||||||
if (!(context.SyntaxReceiver is MapToSyntaxReceiver receiver) || !receiver.CandidateClasses.Any())
|
if (context.SyntaxReceiver is MapToSyntaxReceiver receiver && receiver.CandidateClasses.Any())
|
||||||
{
|
{
|
||||||
return;
|
AddGeneratedMappingsClasses(context, receiver.CandidateClasses);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var classDeclarationSyntax in receiver.CandidateClasses)
|
private static void AddGeneratedMappingsClasses(GeneratorExecutionContext context, IEnumerable<ClassDeclarationSyntax> candidateClasses)
|
||||||
|
{
|
||||||
|
foreach (var classDeclarationSyntax in candidateClasses)
|
||||||
{
|
{
|
||||||
var (model, diagnostic) = GetModel(context.Compilation, classDeclarationSyntax);
|
var (model, diagnostic) = MapModel.Create(context.Compilation, classDeclarationSyntax);
|
||||||
if (model is null)
|
if (model is null)
|
||||||
{
|
{
|
||||||
context.ReportDiagnostic(diagnostic!);
|
context.ReportDiagnostic(diagnostic!);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var (source, hintName) = SourceBuilder.GenerateSource(model);
|
var (source, hintName) = SourceBuilder.GenerateSource(model);
|
||||||
|
|
||||||
context.AddSource(hintName, source);
|
context.AddSource(hintName, source);
|
||||||
context.ReportDiagnostic(Diagnostics.ClassMappingsGenerated(classDeclarationSyntax.GetLocation(), model.ClassName));
|
context.ReportDiagnostic(Diagnostics.ClassMappingsGenerated(classDeclarationSyntax.GetLocation(), model.ClassName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (MapModel? model, Diagnostic? diagnostic) GetModel(Compilation compilation, ClassDeclarationSyntax classSyntax)
|
private static void AddMapFromAttribute(GeneratorExecutionContext context)
|
||||||
{
|
{
|
||||||
var root = classSyntax.GetCompilationUnit();
|
var (source, hintName) = SourceBuilder.GenerateMapFromAttribute();
|
||||||
var classSemanticModel = compilation.GetSemanticModel(classSyntax.SyntaxTree);
|
context.AddSource(hintName, source);
|
||||||
|
|
||||||
if (!(classSemanticModel.GetDeclaredSymbol(classSyntax) is INamedTypeSymbol classSymbol))
|
|
||||||
{
|
|
||||||
return (default, Diagnostics.SymbolNotFound(classSyntax.GetLocation(), classSyntax.Identifier.ValueText));
|
|
||||||
}
|
|
||||||
|
|
||||||
var sourceTypeSymbol = GetSourceTypeSymbol(classSyntax, classSemanticModel);
|
|
||||||
if (sourceTypeSymbol is null)
|
|
||||||
{
|
|
||||||
return (default, Diagnostics.SymbolNotFound(classSyntax.GetLocation(), classSyntax.Identifier.ValueText));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (MapModel.Create(root, classSyntax, classSymbol, sourceTypeSymbol), default);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ITypeSymbol? GetSourceTypeSymbol(ClassDeclarationSyntax classSyntax, SemanticModel model)
|
|
||||||
{
|
|
||||||
var sourceTypeExpressionSyntax = classSyntax
|
|
||||||
.GetAttribute(SourceBuilder.MapFromAttributeName)
|
|
||||||
?.DescendantNodes()
|
|
||||||
.OfType<TypeOfExpressionSyntax>()
|
|
||||||
.SingleOrDefault();
|
|
||||||
|
|
||||||
return sourceTypeExpressionSyntax is not null ? model.GetTypeInfo(sourceTypeExpressionSyntax.Type).Type : null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<AssemblyName>MapTo</AssemblyName>
|
<AssemblyName>MapTo</AssemblyName>
|
||||||
<Description>Generates mapping code between two types using Roslyn code generator.</Description>
|
<Description>An object to object mapping generator using using Roslyn code generator.</Description>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
<IncludeSymbols>true</IncludeSymbols>
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
<NoWarn>NU5128</NoWarn>
|
|
||||||
<PackageId>MapTo</PackageId>
|
<PackageId>MapTo</PackageId>
|
||||||
|
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||||
<PackageProjectUrl>https://github.com/mrtaikandi/mapto</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/mrtaikandi/mapto</PackageProjectUrl>
|
||||||
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
||||||
<PackageVersion>$(Version)</PackageVersion>
|
<PackageVersion>$(Version)</PackageVersion>
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
<RootNamespace>MapTo</RootNamespace>
|
<RootNamespace>MapTo</RootNamespace>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>9</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -32,6 +32,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<None Include="..\LICENSE" Pack="true" PackagePath="" Visible="false" />
|
||||||
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using MapTo.Extensions;
|
using MapTo.Extensions;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
@ -43,9 +44,23 @@ namespace MapTo.Models
|
||||||
|
|
||||||
public IEnumerable<IPropertySymbol> SourceTypeProperties { get; }
|
public IEnumerable<IPropertySymbol> SourceTypeProperties { get; }
|
||||||
|
|
||||||
internal static MapModel Create(CompilationUnitSyntax root, ClassDeclarationSyntax classSyntax, INamedTypeSymbol classSymbol, ITypeSymbol sourceTypeSymbol)
|
internal static (MapModel? model, Diagnostic? diagnostic) Create(Compilation compilation, ClassDeclarationSyntax classSyntax)
|
||||||
{
|
{
|
||||||
return new(
|
var root = classSyntax.GetCompilationUnit();
|
||||||
|
var classSemanticModel = compilation.GetSemanticModel(classSyntax.SyntaxTree);
|
||||||
|
|
||||||
|
if (!(classSemanticModel.GetDeclaredSymbol(classSyntax) is INamedTypeSymbol classSymbol))
|
||||||
|
{
|
||||||
|
return (default, Diagnostics.SymbolNotFound(classSyntax.GetLocation(), classSyntax.Identifier.ValueText));
|
||||||
|
}
|
||||||
|
|
||||||
|
var sourceTypeSymbol = GetSourceTypeSymbol(classSyntax, classSemanticModel);
|
||||||
|
if (sourceTypeSymbol is null)
|
||||||
|
{
|
||||||
|
return (default, Diagnostics.SymbolNotFound(classSyntax.GetLocation(), classSyntax.Identifier.ValueText));
|
||||||
|
}
|
||||||
|
|
||||||
|
var model = new MapModel(
|
||||||
root.GetNamespace(),
|
root.GetNamespace(),
|
||||||
classSyntax.Modifiers,
|
classSyntax.Modifiers,
|
||||||
classSyntax.GetClassName(),
|
classSyntax.GetClassName(),
|
||||||
|
@ -54,6 +69,19 @@ namespace MapTo.Models
|
||||||
sourceTypeSymbol.Name,
|
sourceTypeSymbol.Name,
|
||||||
sourceTypeSymbol.ToString(),
|
sourceTypeSymbol.ToString(),
|
||||||
sourceTypeSymbol.GetAllMembersOfType<IPropertySymbol>());
|
sourceTypeSymbol.GetAllMembersOfType<IPropertySymbol>());
|
||||||
|
|
||||||
|
return (model, default);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ITypeSymbol? GetSourceTypeSymbol(ClassDeclarationSyntax classSyntax, SemanticModel model)
|
||||||
|
{
|
||||||
|
var sourceTypeExpressionSyntax = classSyntax
|
||||||
|
.GetAttribute(SourceBuilder.MapFromAttributeName)
|
||||||
|
?.DescendantNodes()
|
||||||
|
.OfType<TypeOfExpressionSyntax>()
|
||||||
|
.SingleOrDefault();
|
||||||
|
|
||||||
|
return sourceTypeExpressionSyntax is not null ? model.GetTypeInfo(sourceTypeExpressionSyntax.Type).Type : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,7 +16,7 @@ namespace MapTo
|
||||||
private const int Indent2 = Indent1 * 2; // " ";
|
private const int Indent2 = Indent1 * 2; // " ";
|
||||||
private const int Indent3 = Indent1 * 3; // " ";
|
private const int Indent3 = Indent1 * 3; // " ";
|
||||||
|
|
||||||
internal static void AddMapToAttribute(this GeneratorExecutionContext context)
|
internal static (string source, string hintName) GenerateMapFromAttribute()
|
||||||
{
|
{
|
||||||
const string source = @"
|
const string source = @"
|
||||||
using System;
|
using System;
|
||||||
|
@ -36,7 +36,7 @@ namespace MapTo
|
||||||
}
|
}
|
||||||
";
|
";
|
||||||
|
|
||||||
context.AddSource("MapFromAttribute.g.cs", source);
|
return (source, $"{MapFromAttributeName}Attribute.g.cs");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static (string source, string hintName) GenerateSource(MapModel model)
|
internal static (string source, string hintName) GenerateSource(MapModel model)
|
||||||
|
@ -70,13 +70,13 @@ namespace MapTo
|
||||||
.PadLeft(Indent1)
|
.PadLeft(Indent1)
|
||||||
.AppendFormat("{0} static partial class {1}Extensions", model.ClassModifiers.FirstOrDefault().ToFullString().Trim(), model.SourceClassName)
|
.AppendFormat("{0} static partial class {1}Extensions", model.ClassModifiers.FirstOrDefault().ToFullString().Trim(), model.SourceClassName)
|
||||||
.AppendOpeningBracket(Indent1)
|
.AppendOpeningBracket(Indent1)
|
||||||
|
|
||||||
// Extension class body
|
// Extension class body
|
||||||
.GenerateSourceTypeExtensionMethod(model)
|
.GenerateSourceTypeExtensionMethod(model)
|
||||||
|
|
||||||
// End extensions class declaration
|
// End extensions class declaration
|
||||||
.AppendClosingBracket(Indent1)
|
.AppendClosingBracket(Indent1)
|
||||||
|
|
||||||
// End namespace declaration
|
// End namespace declaration
|
||||||
.AppendClosingBracket();
|
.AppendClosingBracket();
|
||||||
|
|
||||||
|
@ -150,11 +150,9 @@ namespace MapTo
|
||||||
.AppendFormat("return {0} == null ? null : new {1}({0});", sourceClassParameterName, model.ClassName)
|
.AppendFormat("return {0} == null ? null : new {1}({0});", sourceClassParameterName, model.ClassName)
|
||||||
.AppendClosingBracket(Indent2);
|
.AppendClosingBracket(Indent2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static StringBuilder AppendFileHeader(this StringBuilder builder)
|
private static StringBuilder AppendFileHeader(this StringBuilder builder) =>
|
||||||
{
|
builder
|
||||||
return builder
|
|
||||||
.AppendLine("// <auto-generated />");
|
.AppendLine("// <auto-generated />");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,17 +11,6 @@ namespace MapToTests
|
||||||
{
|
{
|
||||||
internal static class CSharpGenerator
|
internal static class CSharpGenerator
|
||||||
{
|
{
|
||||||
internal static string GetGeneratedOutput(this ITestOutputHelper outputHelper, string source)
|
|
||||||
{
|
|
||||||
var (compilation, diagnostics) = GetOutputCompilation(source);
|
|
||||||
diagnostics.ShouldBeSuccessful();
|
|
||||||
|
|
||||||
var generatedOutput = compilation.SyntaxTrees.Last().ToString();
|
|
||||||
outputHelper.WriteLine(generatedOutput);
|
|
||||||
|
|
||||||
return generatedOutput;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void ShouldBeSuccessful(this ImmutableArray<Diagnostic> diagnostics)
|
internal static void ShouldBeSuccessful(this ImmutableArray<Diagnostic> diagnostics)
|
||||||
{
|
{
|
||||||
Assert.False(diagnostics.Any(d => d.Severity >= DiagnosticSeverity.Warning), $"Failed: {Environment.NewLine}{string.Join($"{Environment.NewLine}- ", diagnostics.Select(c => c.GetMessage()))}");
|
Assert.False(diagnostics.Any(d => d.Severity >= DiagnosticSeverity.Warning), $"Failed: {Environment.NewLine}{string.Join($"{Environment.NewLine}- ", diagnostics.Select(c => c.GetMessage()))}");
|
||||||
|
@ -47,7 +36,7 @@ namespace MapToTests
|
||||||
ISourceGenerator generator = new MapToGenerator();
|
ISourceGenerator generator = new MapToGenerator();
|
||||||
var driver = CSharpGeneratorDriver.Create(generator);
|
var driver = CSharpGeneratorDriver.Create(generator);
|
||||||
driver.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out var generateDiagnostics);
|
driver.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out var generateDiagnostics);
|
||||||
|
|
||||||
return (outputCompilation, generateDiagnostics);
|
return (outputCompilation, generateDiagnostics);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,13 @@ namespace MapToTests
|
||||||
{
|
{
|
||||||
public class Tests
|
public class Tests
|
||||||
{
|
{
|
||||||
|
public Tests(ITestOutputHelper output)
|
||||||
|
{
|
||||||
|
_output = output;
|
||||||
|
}
|
||||||
|
|
||||||
private readonly ITestOutputHelper _output;
|
private readonly ITestOutputHelper _output;
|
||||||
|
|
||||||
private const string ExpectedAttribute = @"
|
private const string ExpectedAttribute = @"
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
|
@ -26,225 +32,6 @@ namespace MapTo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
";
|
";
|
||||||
|
|
||||||
public Tests(ITestOutputHelper output)
|
|
||||||
{
|
|
||||||
_output = output;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void VerifyMapToAttribute()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
const string source = "";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
diagnostics.ShouldBeSuccessful();
|
|
||||||
compilation.SyntaxTrees.ShouldContain(c => c.ToString() == ExpectedAttribute);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void When_NoMapToAttributeFound_Should_GenerateOnlyTheAttribute()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
const string source = "";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
diagnostics.ShouldBeSuccessful();
|
|
||||||
compilation.SyntaxTrees.ShouldContain(s => s.ToString() == ExpectedAttribute);
|
|
||||||
compilation.SyntaxTrees.Select(s => s.ToString()).Where(s => s != string.Empty && s != ExpectedAttribute).ShouldBeEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void When_MapToAttributeFound_Should_GenerateTheClass()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
const string source = @"
|
|
||||||
using MapTo;
|
|
||||||
|
|
||||||
namespace Test
|
|
||||||
{
|
|
||||||
[MapFrom(typeof(Baz))]
|
|
||||||
public partial class Foo
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Baz
|
|
||||||
{
|
|
||||||
public int Prop1 { get; set; }
|
|
||||||
public int Prop2 { get; }
|
|
||||||
public int Prop3 { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
";
|
|
||||||
|
|
||||||
const string expectedResult = @"
|
|
||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Test
|
|
||||||
{
|
|
||||||
public partial class Foo
|
|
||||||
{
|
|
||||||
public Foo(Test.Baz baz)
|
|
||||||
{
|
|
||||||
if (baz == null) throw new ArgumentNullException(nameof(baz));
|
|
||||||
}
|
|
||||||
";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
diagnostics.ShouldBeSuccessful();
|
|
||||||
compilation.SyntaxTrees.Count().ShouldBe(3);
|
|
||||||
compilation.SyntaxTrees.Last().ToString().ShouldStartWith(expectedResult.Trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void When_MapToAttributeWithNamespaceFound_Should_GenerateTheClass()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
const string source = @"
|
|
||||||
namespace Test
|
|
||||||
{
|
|
||||||
[MapTo.MapFrom(typeof(Baz))]
|
|
||||||
public partial class Foo
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Baz
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
";
|
|
||||||
|
|
||||||
const string expectedResult = @"
|
|
||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Test
|
|
||||||
{
|
|
||||||
public partial class Foo
|
|
||||||
{
|
|
||||||
public Foo(Test.Baz baz)
|
|
||||||
{
|
|
||||||
if (baz == null) throw new ArgumentNullException(nameof(baz));
|
|
||||||
}
|
|
||||||
";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
diagnostics.ShouldBeSuccessful();
|
|
||||||
compilation.SyntaxTrees.Count().ShouldBe(3);
|
|
||||||
compilation.SyntaxTrees.Last().ToString().ShouldStartWith(expectedResult.Trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void When_SourceTypeHasMatchingProperties_Should_CreateConstructorAndAssignSrcToDest()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var source = GetSourceText();
|
|
||||||
|
|
||||||
const string expectedResult = @"
|
|
||||||
public partial class Foo
|
|
||||||
{
|
|
||||||
public Foo(Test.Models.Baz baz)
|
|
||||||
{
|
|
||||||
if (baz == null) throw new ArgumentNullException(nameof(baz));
|
|
||||||
Prop1 = baz.Prop1;
|
|
||||||
Prop2 = baz.Prop2;
|
|
||||||
Prop3 = baz.Prop3;
|
|
||||||
}
|
|
||||||
";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
diagnostics.ShouldBeSuccessful();
|
|
||||||
compilation.SyntaxTrees.Count().ShouldBe(3);
|
|
||||||
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult.Trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void When_SourceTypeHasMatchingProperties_Should_CreateFromStaticMethod()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var source = GetSourceText();
|
|
||||||
|
|
||||||
const string expectedResult = @"
|
|
||||||
public static Foo From(Test.Models.Baz baz)
|
|
||||||
{
|
|
||||||
return baz == null ? null : new Foo(baz);
|
|
||||||
}
|
|
||||||
";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
diagnostics.ShouldBeSuccessful();
|
|
||||||
compilation.SyntaxTrees.Count().ShouldBe(3);
|
|
||||||
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult.Trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void When_SourceTypeHasDifferentNamespace_Should_AddToUsings()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var source = GetSourceText(sourceClassNamespace: "Bazaar");
|
|
||||||
|
|
||||||
const string expectedResult = @"
|
|
||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Bazaar;
|
|
||||||
";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
diagnostics.ShouldBeSuccessful();
|
|
||||||
compilation.SyntaxTrees.Count().ShouldBe(3);
|
|
||||||
compilation.SyntaxTrees.Last().ToString().ShouldStartWith(expectedResult.Trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void When_SourceTypeHasMatchingProperties_Should_GenerateToExtensionMethodOnSourceType()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var source = GetSourceText();
|
|
||||||
|
|
||||||
const string expectedResult = @"
|
|
||||||
public static class BazExtensions
|
|
||||||
{
|
|
||||||
public static Foo ToFoo(this Test.Models.Baz baz)
|
|
||||||
{
|
|
||||||
return baz == null ? null : new Foo(baz);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
diagnostics.ShouldBeSuccessful();
|
|
||||||
compilation.SyntaxTrees.Count().ShouldBe(3);
|
|
||||||
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult.Trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetSourceText(bool includeAttributeNamespace = false, string sourceClassNamespace = "Test.Models")
|
private static string GetSourceText(bool includeAttributeNamespace = false, string sourceClassNamespace = "Test.Models")
|
||||||
{
|
{
|
||||||
|
@ -253,7 +40,7 @@ using Bazaar;
|
||||||
{(includeAttributeNamespace ? string.Empty : "using MapTo;")}
|
{(includeAttributeNamespace ? string.Empty : "using MapTo;")}
|
||||||
namespace Test
|
namespace Test
|
||||||
{{
|
{{
|
||||||
{(sourceClassNamespace != "Test" && !includeAttributeNamespace ? $"using {sourceClassNamespace};": string.Empty)}
|
{(sourceClassNamespace != "Test" && !includeAttributeNamespace ? $"using {sourceClassNamespace};" : string.Empty)}
|
||||||
|
|
||||||
{(includeAttributeNamespace ? "[MapTo.MapFrom(typeof(Baz))]" : "[MapFrom(typeof(Baz))]")}
|
{(includeAttributeNamespace ? "[MapTo.MapFrom(typeof(Baz))]" : "[MapFrom(typeof(Baz))]")}
|
||||||
public partial class Foo
|
public partial class Foo
|
||||||
|
@ -279,5 +66,219 @@ namespace {sourceClassNamespace}
|
||||||
|
|
||||||
return builder.ToString();
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void VerifyMapToAttribute()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const string source = "";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
diagnostics.ShouldBeSuccessful();
|
||||||
|
compilation.SyntaxTrees.ShouldContain(c => c.ToString() == ExpectedAttribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void When_MapToAttributeFound_Should_GenerateTheClass()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const string source = @"
|
||||||
|
using MapTo;
|
||||||
|
|
||||||
|
namespace Test
|
||||||
|
{
|
||||||
|
[MapFrom(typeof(Baz))]
|
||||||
|
public partial class Foo
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Baz
|
||||||
|
{
|
||||||
|
public int Prop1 { get; set; }
|
||||||
|
public int Prop2 { get; }
|
||||||
|
public int Prop3 { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
const string expectedResult = @"
|
||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Test
|
||||||
|
{
|
||||||
|
public partial class Foo
|
||||||
|
{
|
||||||
|
public Foo(Test.Baz baz)
|
||||||
|
{
|
||||||
|
if (baz == null) throw new ArgumentNullException(nameof(baz));
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
diagnostics.ShouldBeSuccessful();
|
||||||
|
compilation.SyntaxTrees.Count().ShouldBe(3);
|
||||||
|
compilation.SyntaxTrees.Last().ToString().ShouldStartWith(expectedResult.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void When_MapToAttributeWithNamespaceFound_Should_GenerateTheClass()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const string source = @"
|
||||||
|
namespace Test
|
||||||
|
{
|
||||||
|
[MapTo.MapFrom(typeof(Baz))]
|
||||||
|
public partial class Foo
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Baz
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
const string expectedResult = @"
|
||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Test
|
||||||
|
{
|
||||||
|
public partial class Foo
|
||||||
|
{
|
||||||
|
public Foo(Test.Baz baz)
|
||||||
|
{
|
||||||
|
if (baz == null) throw new ArgumentNullException(nameof(baz));
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
diagnostics.ShouldBeSuccessful();
|
||||||
|
compilation.SyntaxTrees.Count().ShouldBe(3);
|
||||||
|
compilation.SyntaxTrees.Last().ToString().ShouldStartWith(expectedResult.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void When_NoMapToAttributeFound_Should_GenerateOnlyTheAttribute()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const string source = "";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
diagnostics.ShouldBeSuccessful();
|
||||||
|
compilation.SyntaxTrees.ShouldContain(s => s.ToString() == ExpectedAttribute);
|
||||||
|
compilation.SyntaxTrees.Select(s => s.ToString()).Where(s => s != string.Empty && s != ExpectedAttribute).ShouldBeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void When_SourceTypeHasDifferentNamespace_Should_AddToUsings()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var source = GetSourceText(sourceClassNamespace: "Bazaar");
|
||||||
|
|
||||||
|
const string expectedResult = @"
|
||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Bazaar;
|
||||||
|
";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
diagnostics.ShouldBeSuccessful();
|
||||||
|
compilation.SyntaxTrees.Count().ShouldBe(3);
|
||||||
|
compilation.SyntaxTrees.Last().ToString().ShouldStartWith(expectedResult.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void When_SourceTypeHasMatchingProperties_Should_CreateConstructorAndAssignSrcToDest()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var source = GetSourceText();
|
||||||
|
|
||||||
|
const string expectedResult = @"
|
||||||
|
public partial class Foo
|
||||||
|
{
|
||||||
|
public Foo(Test.Models.Baz baz)
|
||||||
|
{
|
||||||
|
if (baz == null) throw new ArgumentNullException(nameof(baz));
|
||||||
|
Prop1 = baz.Prop1;
|
||||||
|
Prop2 = baz.Prop2;
|
||||||
|
Prop3 = baz.Prop3;
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
diagnostics.ShouldBeSuccessful();
|
||||||
|
compilation.SyntaxTrees.Count().ShouldBe(3);
|
||||||
|
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void When_SourceTypeHasMatchingProperties_Should_CreateFromStaticMethod()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var source = GetSourceText();
|
||||||
|
|
||||||
|
const string expectedResult = @"
|
||||||
|
public static Foo From(Test.Models.Baz baz)
|
||||||
|
{
|
||||||
|
return baz == null ? null : new Foo(baz);
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
diagnostics.ShouldBeSuccessful();
|
||||||
|
compilation.SyntaxTrees.Count().ShouldBe(3);
|
||||||
|
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void When_SourceTypeHasMatchingProperties_Should_GenerateToExtensionMethodOnSourceType()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var source = GetSourceText();
|
||||||
|
|
||||||
|
const string expectedResult = @"
|
||||||
|
public static partial class BazExtensions
|
||||||
|
{
|
||||||
|
public static Foo ToFoo(this Test.Models.Baz baz)
|
||||||
|
{
|
||||||
|
return baz == null ? null : new Foo(baz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
diagnostics.ShouldBeSuccessful();
|
||||||
|
compilation.SyntaxTrees.Count().ShouldBe(3);
|
||||||
|
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult.Trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
# MapTo
|
||||||
|
An object to object mapping generator using using Roslyn code generator.
|
|
@ -1,15 +1,13 @@
|
||||||
using System;
|
using TestConsoleApp.ViewModels;
|
||||||
using TestConsoleApp.ViewModels;
|
|
||||||
|
|
||||||
namespace TestConsoleApp
|
namespace TestConsoleApp
|
||||||
{
|
{
|
||||||
class Program
|
internal class Program
|
||||||
{
|
{
|
||||||
static void Main(string[] args)
|
private static void Main(string[] args)
|
||||||
{
|
{
|
||||||
var userViewModel = User.From(new Data.Models.User());
|
var userViewModel = User.From(new Data.Models.User());
|
||||||
var userViewModel2 = UserViewModel.From(new Data.Models.User());
|
var userViewModel2 = UserViewModel.From(new Data.Models.User());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,10 @@
|
||||||
|
// using MapTo;
|
||||||
// using MapTo;
|
|
||||||
|
using MapTo;
|
||||||
|
|
||||||
namespace TestConsoleApp.ViewModels
|
namespace TestConsoleApp.ViewModels
|
||||||
{
|
{
|
||||||
[MapTo.MapFrom(typeof(Data.Models.User))]
|
[MapFrom(typeof(Data.Models.User))]
|
||||||
public partial class User
|
public partial class User
|
||||||
{
|
{
|
||||||
public string FirstName { get; }
|
public string FirstName { get; }
|
||||||
|
|
|
@ -3,8 +3,5 @@
|
||||||
namespace TestConsoleApp.ViewModels
|
namespace TestConsoleApp.ViewModels
|
||||||
{
|
{
|
||||||
[MapFrom(typeof(Data.Models.User))]
|
[MapFrom(typeof(Data.Models.User))]
|
||||||
public partial class UserViewModel
|
public partial class UserViewModel { }
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue