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.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
internal static class IsExternalInit
|
||||
{
|
||||
}
|
||||
internal static class IsExternalInit { }
|
||||
}
|
|
@ -6,16 +6,16 @@ namespace MapTo
|
|||
{
|
||||
private const string UsageCategory = "Usage";
|
||||
|
||||
internal static Diagnostic SymbolNotFound(Location location, string syntaxName) =>
|
||||
Diagnostic.Create(CreateDescriptor("MT0001", "Symbol not found.", $"Unable to find any symbols for {syntaxName}"), location);
|
||||
internal static Diagnostic SymbolNotFound(Location location, string syntaxName) =>
|
||||
Create("MT0001", "Symbol not found.", $"Unable to find any symbols for {syntaxName}", location);
|
||||
|
||||
internal static Diagnostic MapFromAttributeNotFound(Location location) =>
|
||||
Diagnostic.Create(CreateDescriptor("MT0002", "Attribute Not Available", $"Unable to find {SourceBuilder.MapFromAttributeName} type."), location);
|
||||
internal static Diagnostic MapFromAttributeNotFound(Location location) =>
|
||||
Create("MT0002", "Attribute Not Available", $"Unable to find {SourceBuilder.MapFromAttributeName} type.", location);
|
||||
|
||||
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) =>
|
||||
new(id, title, message, UsageCategory, severity, true);
|
||||
private static Diagnostic Create(string id, string title, string message, Location location, DiagnosticSeverity severity = DiagnosticSeverity.Error) =>
|
||||
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());
|
||||
}
|
||||
|
||||
public static IEnumerable<T> GetAllMembersOfType<T>(this ITypeSymbol type) where T : ISymbol
|
||||
{
|
||||
return type.GetAllMembers().OfType<T>();
|
||||
}
|
||||
public static IEnumerable<T> GetAllMembersOfType<T>(this ITypeSymbol type) where T : ISymbol => type.GetAllMembers().OfType<T>();
|
||||
|
||||
public static CompilationUnitSyntax GetCompilationUnit(this SyntaxNode syntaxNode)
|
||||
{
|
||||
return syntaxNode.Ancestors().OfType<CompilationUnitSyntax>().Single();
|
||||
}
|
||||
public static CompilationUnitSyntax GetCompilationUnit(this SyntaxNode syntaxNode) => syntaxNode.Ancestors().OfType<CompilationUnitSyntax>().Single();
|
||||
|
||||
public static string GetClassName(this ClassDeclarationSyntax classSyntax)
|
||||
{
|
||||
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 string GetClassName(this ClassDeclarationSyntax classSyntax) => classSyntax.Identifier.Text;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public static string? GetNamespace(this CompilationUnitSyntax root)
|
||||
{
|
||||
return root.ChildNodes()
|
||||
public static string? GetNamespace(this CompilationUnitSyntax root) =>
|
||||
root.ChildNodes()
|
||||
.OfType<NamespaceDeclarationSyntax>()
|
||||
.FirstOrDefault()
|
||||
?.Name
|
||||
.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;
|
||||
}
|
||||
|
||||
internal static StringBuilder AppendOpeningBracket(this StringBuilder builder, int indent = 0)
|
||||
{
|
||||
return builder.AppendLine().PadLeft(indent).AppendFormat("{{{0}", Environment.NewLine);
|
||||
}
|
||||
internal static StringBuilder AppendOpeningBracket(this StringBuilder builder, int indent = 0) => builder.AppendLine().PadLeft(indent).AppendFormat("{{{0}", Environment.NewLine);
|
||||
|
||||
internal static StringBuilder AppendClosingBracket(this StringBuilder builder, int indent = 0, bool padNewLine = true)
|
||||
{
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
{
|
||||
internal static class StringExtensions
|
||||
{
|
||||
public static string ToCamelCase(this string value)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(value) ? value : $"{char.ToLower(value[0])}{value.Substring(1)}";
|
||||
}
|
||||
public static string ToCamelCase(this string value) => string.IsNullOrWhiteSpace(value) ? value : $"{char.ToLower(value[0])}{value.Substring(1)}";
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MapTo.Extensions;
|
||||
using MapTo.Models;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
@ -19,57 +18,36 @@ namespace MapTo
|
|||
/// <inheritdoc />
|
||||
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)
|
||||
{
|
||||
context.ReportDiagnostic(diagnostic!);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
var (source, hintName) = SourceBuilder.GenerateSource(model);
|
||||
|
||||
|
||||
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 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));
|
||||
}
|
||||
|
||||
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;
|
||||
var (source, hintName) = SourceBuilder.GenerateMapFromAttribute();
|
||||
context.AddSource(hintName, source);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<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>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<NoWarn>NU5128</NoWarn>
|
||||
<PackageId>MapTo</PackageId>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<PackageProjectUrl>https://github.com/mrtaikandi/mapto</PackageProjectUrl>
|
||||
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
||||
<PackageVersion>$(Version)</PackageVersion>
|
||||
|
@ -16,7 +16,7 @@
|
|||
<RootNamespace>MapTo</RootNamespace>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<LangVersion>9</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -32,6 +32,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\LICENSE" Pack="true" PackagePath="" Visible="false" />
|
||||
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MapTo.Extensions;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
@ -43,9 +44,23 @@ namespace MapTo.Models
|
|||
|
||||
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(),
|
||||
classSyntax.Modifiers,
|
||||
classSyntax.GetClassName(),
|
||||
|
@ -54,6 +69,19 @@ namespace MapTo.Models
|
|||
sourceTypeSymbol.Name,
|
||||
sourceTypeSymbol.ToString(),
|
||||
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 Indent3 = Indent1 * 3; // " ";
|
||||
|
||||
internal static void AddMapToAttribute(this GeneratorExecutionContext context)
|
||||
internal static (string source, string hintName) GenerateMapFromAttribute()
|
||||
{
|
||||
const string source = @"
|
||||
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)
|
||||
|
@ -70,13 +70,13 @@ namespace MapTo
|
|||
.PadLeft(Indent1)
|
||||
.AppendFormat("{0} static partial class {1}Extensions", model.ClassModifiers.FirstOrDefault().ToFullString().Trim(), model.SourceClassName)
|
||||
.AppendOpeningBracket(Indent1)
|
||||
|
||||
|
||||
// Extension class body
|
||||
.GenerateSourceTypeExtensionMethod(model)
|
||||
|
||||
|
||||
// End extensions class declaration
|
||||
.AppendClosingBracket(Indent1)
|
||||
|
||||
|
||||
// End namespace declaration
|
||||
.AppendClosingBracket();
|
||||
|
||||
|
@ -150,11 +150,9 @@ namespace MapTo
|
|||
.AppendFormat("return {0} == null ? null : new {1}({0});", sourceClassParameterName, model.ClassName)
|
||||
.AppendClosingBracket(Indent2);
|
||||
}
|
||||
|
||||
private static StringBuilder AppendFileHeader(this StringBuilder builder)
|
||||
{
|
||||
return builder
|
||||
|
||||
private static StringBuilder AppendFileHeader(this StringBuilder builder) =>
|
||||
builder
|
||||
.AppendLine("// <auto-generated />");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,17 +11,6 @@ namespace MapToTests
|
|||
{
|
||||
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)
|
||||
{
|
||||
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();
|
||||
var driver = CSharpGeneratorDriver.Create(generator);
|
||||
driver.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out var generateDiagnostics);
|
||||
|
||||
|
||||
return (outputCompilation, generateDiagnostics);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,13 @@ namespace MapToTests
|
|||
{
|
||||
public class Tests
|
||||
{
|
||||
public Tests(ITestOutputHelper output)
|
||||
{
|
||||
_output = output;
|
||||
}
|
||||
|
||||
private readonly ITestOutputHelper _output;
|
||||
|
||||
private const string ExpectedAttribute = @"
|
||||
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")
|
||||
{
|
||||
|
@ -253,7 +40,7 @@ using Bazaar;
|
|||
{(includeAttributeNamespace ? string.Empty : "using MapTo;")}
|
||||
namespace Test
|
||||
{{
|
||||
{(sourceClassNamespace != "Test" && !includeAttributeNamespace ? $"using {sourceClassNamespace};": string.Empty)}
|
||||
{(sourceClassNamespace != "Test" && !includeAttributeNamespace ? $"using {sourceClassNamespace};" : string.Empty)}
|
||||
|
||||
{(includeAttributeNamespace ? "[MapTo.MapFrom(typeof(Baz))]" : "[MapFrom(typeof(Baz))]")}
|
||||
public partial class Foo
|
||||
|
@ -279,5 +66,219 @@ namespace {sourceClassNamespace}
|
|||
|
||||
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
|
||||
{
|
||||
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 userViewModel2 = UserViewModel.From(new Data.Models.User());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
|
||||
// using MapTo;
|
||||
// using MapTo;
|
||||
|
||||
using MapTo;
|
||||
|
||||
namespace TestConsoleApp.ViewModels
|
||||
{
|
||||
[MapTo.MapFrom(typeof(Data.Models.User))]
|
||||
[MapFrom(typeof(Data.Models.User))]
|
||||
public partial class User
|
||||
{
|
||||
public string FirstName { get; }
|
||||
|
|
|
@ -3,8 +3,5 @@
|
|||
namespace TestConsoleApp.ViewModels
|
||||
{
|
||||
[MapFrom(typeof(Data.Models.User))]
|
||||
public partial class UserViewModel
|
||||
{
|
||||
|
||||
}
|
||||
public partial class UserViewModel { }
|
||||
}
|
Loading…
Reference in New Issue