Add TypeConverter interface.
This commit is contained in:
parent
8a3bbc095e
commit
e9e44b979a
|
@ -1,3 +1,6 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Mapto/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=paramref/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Shouldly/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=typeparam/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Usings/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using MapTo.Sources;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace MapTo.Extensions
|
||||
|
@ -33,5 +34,8 @@ namespace MapTo.Extensions
|
|||
}
|
||||
|
||||
internal static string GetBuildPropertyName(string propertyName) => $"build_property.{PropertyNameSuffix}{propertyName}";
|
||||
|
||||
internal static void AddSource(this GeneratorExecutionContext context, Source source) =>
|
||||
context.AddSource(source.HintName, source.Code);
|
||||
}
|
||||
}
|
|
@ -22,17 +22,15 @@ namespace MapTo
|
|||
{
|
||||
var options = SourceGenerationOptions.From(context);
|
||||
|
||||
AddAttribute(context, MapFromAttributeSource.Generate(options));
|
||||
AddAttribute(context, IgnorePropertyAttributeSource.Generate(options));
|
||||
context.AddSource(MapFromAttributeSource.Generate(options));
|
||||
context.AddSource(IgnorePropertyAttributeSource.Generate(options));
|
||||
context.AddSource(TypeConverterSource.Generate(options));
|
||||
|
||||
if (context.SyntaxReceiver is MapToSyntaxReceiver receiver && receiver.CandidateClasses.Any())
|
||||
{
|
||||
AddGeneratedMappingsClasses(context, receiver.CandidateClasses, options);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddAttribute(GeneratorExecutionContext context, (string source, string hintName) attribute)
|
||||
=> context.AddSource(attribute.hintName, attribute.source);
|
||||
|
||||
private static void AddGeneratedMappingsClasses(GeneratorExecutionContext context, IEnumerable<ClassDeclarationSyntax> candidateClasses, SourceGenerationOptions options)
|
||||
{
|
||||
|
@ -42,14 +40,11 @@ namespace MapTo
|
|||
var (model, diagnostics) = MapModel.CreateModel(classSemanticModel, classSyntax, options);
|
||||
|
||||
diagnostics.ForEach(context.ReportDiagnostic);
|
||||
|
||||
if (model is null)
|
||||
|
||||
if (model is not null)
|
||||
{
|
||||
continue;
|
||||
context.AddSource(MapClassSource.Generate(model));
|
||||
}
|
||||
|
||||
var (source, hintName) = MapClassSource.Generate(model);
|
||||
context.AddSource(hintName, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace MapTo.Sources
|
|||
{
|
||||
internal const string AttributeName = "IgnoreProperty";
|
||||
|
||||
internal static (string source, string hintName) Generate(SourceGenerationOptions options)
|
||||
internal static Source Generate(SourceGenerationOptions options)
|
||||
{
|
||||
var builder = new SourceBuilder()
|
||||
.WriteLine(GeneratedFilesHeader)
|
||||
|
@ -29,7 +29,7 @@ namespace MapTo.Sources
|
|||
.WriteLine($"public sealed class {AttributeName}Attribute : Attribute {{ }}")
|
||||
.WriteClosingBracket();
|
||||
|
||||
return (builder.ToString(), $"{AttributeName}Attribute.g.cs");
|
||||
return new(builder.ToString(), $"{AttributeName}Attribute.g.cs");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ namespace MapTo.Sources
|
|||
{
|
||||
internal static class MapClassSource
|
||||
{
|
||||
internal static (string source, string hintName) Generate(MapModel model)
|
||||
internal static Source Generate(MapModel model)
|
||||
{
|
||||
using var builder = new SourceBuilder()
|
||||
.WriteLine(GeneratedFilesHeader)
|
||||
|
@ -36,7 +36,7 @@ namespace MapTo.Sources
|
|||
// End namespace declaration
|
||||
.WriteClosingBracket();
|
||||
|
||||
return (builder.ToString(), $"{model.ClassName}.g.cs");
|
||||
return new(builder.ToString(), $"{model.ClassName}.g.cs");
|
||||
}
|
||||
|
||||
private static SourceBuilder WriteUsings(this SourceBuilder builder, MapModel model)
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace MapTo.Sources
|
|||
{
|
||||
internal const string AttributeName = "MapFrom";
|
||||
|
||||
internal static (string source, string hintName) Generate(SourceGenerationOptions options)
|
||||
internal static Source Generate(SourceGenerationOptions options)
|
||||
{
|
||||
using var builder = new SourceBuilder()
|
||||
.WriteLine(GeneratedFilesHeader)
|
||||
|
@ -57,7 +57,7 @@ namespace MapTo.Sources
|
|||
.WriteClosingBracket() // class
|
||||
.WriteClosingBracket(); // namespace
|
||||
|
||||
return (builder.ToString(), $"{AttributeName}Attribute.g.cs");
|
||||
return new(builder.ToString(), $"{AttributeName}Attribute.g.cs");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
namespace MapTo.Sources
|
||||
{
|
||||
internal record Source(string Code, string HintName);
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
using MapTo.Models;
|
||||
using static MapTo.Sources.Constants;
|
||||
|
||||
namespace MapTo.Sources
|
||||
{
|
||||
internal class TypeConverterSource
|
||||
{
|
||||
internal const string InterfaceName = "ITypeConverter";
|
||||
|
||||
internal static Source Generate(SourceGenerationOptions options)
|
||||
{
|
||||
using var builder = new SourceBuilder()
|
||||
.WriteLine(GeneratedFilesHeader)
|
||||
.WriteLine()
|
||||
.WriteLine($"namespace {RootNamespace}")
|
||||
.WriteOpeningBracket();
|
||||
|
||||
if (options.GenerateXmlDocument)
|
||||
{
|
||||
builder
|
||||
.WriteLine("/// <summary>")
|
||||
.WriteLine("/// Converts the value of <typeparamref name=\"TSource\"/> <typeparamref name=\"TDestination\"/>.")
|
||||
.WriteLine("/// </summary>")
|
||||
.WriteLine("/// <typeparam name=\"TSource\">The type to convert from.</typeparam>")
|
||||
.WriteLine("/// <typeparam name=\"TDestination\">The type to convert to.</typeparam>");
|
||||
}
|
||||
|
||||
builder
|
||||
.WriteLine($"public interface {InterfaceName}<in TSource, out TDestination>")
|
||||
.WriteOpeningBracket();
|
||||
|
||||
if (options.GenerateXmlDocument)
|
||||
{
|
||||
builder
|
||||
.WriteLine("/// <summary>")
|
||||
.WriteLine("/// Converts the value of <paramref name=\"value\"/> object to <typeparamref name=\"TDestination\"/>.")
|
||||
.WriteLine("/// </summary>")
|
||||
.WriteLine("/// <param name=\"value\">The object to convert.</param>")
|
||||
.WriteLine("/// <returns><typeparamref name=\"TDestination\"/> object.</returns>");
|
||||
}
|
||||
|
||||
builder
|
||||
.WriteLine("TDestination Convert(TSource source);")
|
||||
.WriteClosingBracket()
|
||||
.WriteClosingBracket();
|
||||
|
||||
return new(builder.ToString(), $"{InterfaceName}.g.cs");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Shouldly;
|
||||
|
||||
namespace MapTo.Tests.Extensions
|
||||
{
|
||||
internal static class ShouldlyExtensions
|
||||
{
|
||||
internal static void ShouldContainSource(this IEnumerable<SyntaxTree> syntaxTree, string typeName, string expectedSource, string customMessage = null)
|
||||
{
|
||||
var syntax = syntaxTree
|
||||
.Select(s => s.ToString().Trim())
|
||||
.SingleOrDefault(s => s.Contains(typeName));
|
||||
|
||||
syntax.ShouldNotBeNullOrWhiteSpace();
|
||||
syntax.ShouldBe(expectedSource, customMessage);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ using System.Text;
|
|||
using MapTo.Extensions;
|
||||
using MapTo.Models;
|
||||
using MapTo.Sources;
|
||||
using MapTo.Tests.Extensions;
|
||||
using MapTo.Tests.Infrastructure;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Shouldly;
|
||||
|
@ -143,9 +144,7 @@ namespace MapTo
|
|||
|
||||
// Assert
|
||||
diagnostics.ShouldBeSuccessful();
|
||||
var attributeSyntax = compilation.SyntaxTrees.Select(s => s.ToString().Trim()).SingleOrDefault(s => s.Contains(IgnorePropertyAttributeSource.AttributeName));
|
||||
attributeSyntax.ShouldNotBeNullOrWhiteSpace();
|
||||
attributeSyntax.ShouldBe(expectedAttribute);
|
||||
compilation.SyntaxTrees.ShouldContainSource(IgnorePropertyAttributeSource.AttributeName, expectedAttribute);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -159,10 +158,7 @@ namespace MapTo
|
|||
|
||||
// Assert
|
||||
diagnostics.ShouldBeSuccessful();
|
||||
|
||||
var attributeSyntax = compilation.SyntaxTrees.Select(s => s.ToString().Trim()).SingleOrDefault(s => s.Contains(MapFromAttributeSource.AttributeName));
|
||||
attributeSyntax.ShouldNotBeNullOrWhiteSpace();
|
||||
attributeSyntax.ShouldBe(ExpectedAttribute);
|
||||
compilation.SyntaxTrees.ShouldContainSource(MapFromAttributeSource.AttributeName, ExpectedAttribute);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -385,6 +381,7 @@ namespace Test
|
|||
{
|
||||
// Arrange
|
||||
const string source = "";
|
||||
var expectedTypes = new[] { IgnorePropertyAttributeSource.AttributeName, MapFromAttributeSource.AttributeName, TypeConverterSource.InterfaceName };
|
||||
|
||||
// Act
|
||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
||||
|
@ -394,7 +391,7 @@ namespace Test
|
|||
compilation.SyntaxTrees
|
||||
.Select(s => s.ToString())
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s.ToString()))
|
||||
.All(s => s.Contains(": Attribute"))
|
||||
.All(s => expectedTypes.Any(s.Contains))
|
||||
.ShouldBeTrue();
|
||||
}
|
||||
|
||||
|
@ -490,5 +487,30 @@ namespace Test
|
|||
diagnostics.ShouldBeSuccessful();
|
||||
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifyTypeConverterInterface()
|
||||
{
|
||||
// Arrange
|
||||
const string source = "";
|
||||
var expectedInterface = $@"
|
||||
{Constants.GeneratedFilesHeader}
|
||||
|
||||
namespace MapTo
|
||||
{{
|
||||
public interface ITypeConverter<in TSource, out TDestination>
|
||||
{{
|
||||
TDestination Convert(TSource source);
|
||||
}}
|
||||
}}
|
||||
".Trim();
|
||||
|
||||
// Act
|
||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
||||
|
||||
// Assert
|
||||
diagnostics.ShouldBeSuccessful();
|
||||
compilation.SyntaxTrees.ShouldContainSource(TypeConverterSource.InterfaceName, expectedInterface);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue