Add extension method on source type to get the destination type.

This commit is contained in:
Mohammadreza Taikandi 2020-12-19 09:53:31 +00:00
parent 0327ab41a6
commit 85099ed080
4 changed files with 75 additions and 28 deletions

View File

@ -63,7 +63,7 @@ namespace MapTo
return new MapModel( return new MapModel(
ns: root.GetNamespace(), ns: root.GetNamespace(),
classModifiers: classSyntax.GetClassModifier(), classModifiers: classSyntax.Modifiers,
className: classSyntax.GetClassName(), className: classSyntax.GetClassName(),
properties: classSymbol.GetAllMembersOfType<IPropertySymbol>(), properties: classSymbol.GetAllMembersOfType<IPropertySymbol>(),
sourceNamespace: destinationTypeSymbol.ContainingNamespace.Name, sourceNamespace: destinationTypeSymbol.ContainingNamespace.Name,

View File

@ -5,7 +5,7 @@ namespace MapTo.Models
{ {
public class MapModel public class MapModel
{ {
public MapModel(string? ns, string classModifiers, string className, IEnumerable<IPropertySymbol> properties, string sourceNamespace, string sourceClassName, IEnumerable<IPropertySymbol> sourceTypeProperties) public MapModel(string? ns, SyntaxTokenList classModifiers, string className, IEnumerable<IPropertySymbol> properties, string sourceNamespace, string sourceClassName, IEnumerable<IPropertySymbol> sourceTypeProperties)
{ {
Namespace = ns; Namespace = ns;
ClassModifiers = classModifiers; ClassModifiers = classModifiers;
@ -18,7 +18,7 @@ namespace MapTo.Models
public string? Namespace { get; } public string? Namespace { get; }
public string ClassModifiers { get; } public SyntaxTokenList ClassModifiers { get; }
public string ClassName { get; } public string ClassName { get; }

View File

@ -5,7 +5,6 @@ using System.Text;
using MapTo.Extensions; using MapTo.Extensions;
using MapTo.Models; using MapTo.Models;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text;
namespace MapTo namespace MapTo
@ -47,30 +46,40 @@ namespace MapTo
builder builder
.AppendFileHeader() .AppendFileHeader()
.GenerateUsings(model); .GenerateUsings(model)
// Namespace declaration // Namespace declaration
builder
.AppendFormat("namespace {0}", model.Namespace) .AppendFormat("namespace {0}", model.Namespace)
.AppendOpeningBracket(); .AppendOpeningBracket()
// Class declaration // Class declaration
builder
.PadLeft(Indent1) .PadLeft(Indent1)
.AppendFormat("{0} class {1}", model.ClassModifiers, model.ClassName) .AppendFormat("{0} class {1}", model.ClassModifiers.ToFullString().Trim(), model.ClassName)
.AppendOpeningBracket(Indent1); .AppendOpeningBracket(Indent1)
// Class body // Class body
builder
.GenerateConstructor(model, out var mappedProperties) .GenerateConstructor(model, out var mappedProperties)
.AppendLine() .AppendLine()
.GenerateFactoryMethod(model); .GenerateFactoryMethod(model)
// End class declaration // End class declaration
builder.AppendClosingBracket(Indent1); .AppendClosingBracket(Indent1)
// End namespace declaration // Extensions Class declaration
builder.AppendClosingBracket(); .AppendLine()
.AppendLine()
.PadLeft(Indent1)
.AppendFormat("{0} static 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();
return (builder.ToString(), $"{model.ClassName}.cs"); return (builder.ToString(), $"{model.ClassName}.cs");
} }
@ -83,20 +92,20 @@ namespace MapTo
{ {
builder.AppendFormat("using {0};", model.SourceNamespace).AppendLine(); builder.AppendFormat("using {0};", model.SourceNamespace).AppendLine();
} }
return builder.AppendLine(); return builder.AppendLine();
} }
private static StringBuilder GenerateConstructor(this StringBuilder builder, MapModel model, out List<IPropertySymbol> mappedProperties) private static StringBuilder GenerateConstructor(this StringBuilder builder, MapModel model, out List<IPropertySymbol> mappedProperties)
{ {
var destinationClassParameterName = model.SourceClassName.ToCamelCase(); var sourceClassParameterName = model.SourceClassName.ToCamelCase();
builder builder
.PadLeft(Indent2) .PadLeft(Indent2)
.AppendFormat("public {0}({1} {2})", model.ClassName, model.SourceClassName, destinationClassParameterName) .AppendFormat("public {0}({1} {2})", model.ClassName, model.SourceClassName, sourceClassParameterName)
.AppendOpeningBracket(Indent2) .AppendOpeningBracket(Indent2)
.PadLeft(Indent3) .PadLeft(Indent3)
.AppendFormat("if ({0} == null) throw new ArgumentNullException(nameof({0}));", destinationClassParameterName) .AppendFormat("if ({0} == null) throw new ArgumentNullException(nameof({0}));", sourceClassParameterName)
.AppendLine(); .AppendLine();
mappedProperties = new List<IPropertySymbol>(); mappedProperties = new List<IPropertySymbol>();
@ -107,28 +116,41 @@ namespace MapTo
mappedProperties.Add(propertySymbol); mappedProperties.Add(propertySymbol);
builder builder
.PadLeft(Indent3) .PadLeft(Indent3)
.AppendFormat("{0} = {1}.{2};{3}", propertySymbol.Name, destinationClassParameterName, propertySymbol.Name, Environment.NewLine); .AppendFormat("{0} = {1}.{2};{3}", propertySymbol.Name, sourceClassParameterName, propertySymbol.Name, Environment.NewLine);
} }
} }
// End constructor declaration // End constructor declaration
return builder.AppendClosingBracket(Indent2, padNewLine: false); return builder.AppendClosingBracket(Indent2, false);
} }
private static StringBuilder GenerateFactoryMethod(this StringBuilder builder, MapModel model) private static StringBuilder GenerateFactoryMethod(this StringBuilder builder, MapModel model)
{ {
var destinationClassParameterName = model.SourceClassName.ToCamelCase(); var sourceClassParameterName = model.SourceClassName.ToCamelCase();
return builder return builder
.AppendLine() .AppendLine()
.PadLeft(Indent2) .PadLeft(Indent2)
.AppendFormat("public static {0} From({1} {2})", model.ClassName, model.SourceClassName, destinationClassParameterName) .AppendFormat("public static {0} From({1} {2})", model.ClassName, model.SourceClassName, sourceClassParameterName)
.AppendOpeningBracket(Indent2) .AppendOpeningBracket(Indent2)
.PadLeft(Indent3) .PadLeft(Indent3)
.AppendFormat("return {0} == null ? null : new {1}({0});", destinationClassParameterName, model.ClassName) .AppendFormat("return {0} == null ? null : new {1}({0});", sourceClassParameterName, model.ClassName)
.AppendClosingBracket(Indent2); .AppendClosingBracket(Indent2);
} }
private static StringBuilder GenerateSourceTypeExtensionMethod(this StringBuilder builder, MapModel model)
{
var sourceClassParameterName = model.SourceClassName.ToCamelCase();
return builder
.PadLeft(Indent2)
.AppendFormat("public static {0} To{0}(this {1} {2})", model.ClassName, model.SourceClassName, sourceClassParameterName)
.AppendOpeningBracket(Indent2)
.PadLeft(Indent3)
.AppendFormat("return {0} == null ? null : new {1}({0})", sourceClassParameterName, model.ClassName)
.AppendClosingBracket(Indent2);
}
private static StringBuilder AppendFileHeader(this StringBuilder builder) private static StringBuilder AppendFileHeader(this StringBuilder builder)
{ {
return builder return builder

View File

@ -218,6 +218,31 @@ using Bazaar;
compilation.SyntaxTrees.Count().ShouldBe(3); compilation.SyntaxTrees.Count().ShouldBe(3);
compilation.SyntaxTrees.Last().ToString().ShouldStartWith(expectedResult.Trim()); 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 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") private static string GetSourceText(bool includeAttributeNamespace = false, string sourceClassNamespace = "Test")
{ {