Add parameter to ITypeConverter.
This commit is contained in:
parent
2ef7ded450
commit
13c5279e15
|
@ -26,7 +26,7 @@ namespace MapTo
|
||||||
Create($"{ErrorId}031", property.Locations.FirstOrDefault(), "Type Mismatch", $"Cannot create a map for '{property.ToDisplayString()}' property because source and destination types are not implicitly convertible. Consider using '{MapTypeConverterAttributeSource.FullyQualifiedName}' to provide a type converter or ignore the property using '{IgnorePropertyAttributeSource.FullyQualifiedName}'.");
|
Create($"{ErrorId}031", property.Locations.FirstOrDefault(), "Type Mismatch", $"Cannot create a map for '{property.ToDisplayString()}' property because source and destination types are not implicitly convertible. Consider using '{MapTypeConverterAttributeSource.FullyQualifiedName}' to provide a type converter or ignore the property using '{IgnorePropertyAttributeSource.FullyQualifiedName}'.");
|
||||||
|
|
||||||
internal static Diagnostic InvalidTypeConverterGenericTypesError(IPropertySymbol property, IPropertySymbol sourceProperty) =>
|
internal static Diagnostic InvalidTypeConverterGenericTypesError(IPropertySymbol property, IPropertySymbol sourceProperty) =>
|
||||||
Create($"{ErrorId}032", property.Locations.FirstOrDefault(), "Type Mismatch", $"Cannot map '{property.ToDisplayString()}' property because the annotated converter does not implement '{RootNamespace}.{TypeConverterSource.InterfaceName}<{sourceProperty.Type.ToDisplayString()}, {property.Type.ToDisplayString()}>'.");
|
Create($"{ErrorId}032", property.Locations.FirstOrDefault(), "Type Mismatch", $"Cannot map '{property.ToDisplayString()}' property because the annotated converter does not implement '{RootNamespace}.{ITypeConverterSource.InterfaceName}<{sourceProperty.Type.ToDisplayString()}, {property.Type.ToDisplayString()}>'.");
|
||||||
|
|
||||||
internal static Diagnostic ConfigurationParseError(string error) =>
|
internal static Diagnostic ConfigurationParseError(string error) =>
|
||||||
Create($"{ErrorId}040", Location.None, "Incorrect Configuration", error);
|
Create($"{ErrorId}040", Location.None, "Incorrect Configuration", error);
|
||||||
|
|
|
@ -1,7 +1,17 @@
|
||||||
namespace MapTo.Extensions
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace MapTo.Extensions
|
||||||
{
|
{
|
||||||
internal static class StringExtensions
|
internal static class StringExtensions
|
||||||
{
|
{
|
||||||
public static string ToCamelCase(this string value) => 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)}";
|
||||||
|
|
||||||
|
public static string ToSourceCodeString(this object? value) => value switch
|
||||||
|
{
|
||||||
|
null => "null",
|
||||||
|
string strValue => $"\"{strValue}\"",
|
||||||
|
char charValue => $"'{charValue}'",
|
||||||
|
_ => value.ToString()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -24,7 +24,7 @@ namespace MapTo
|
||||||
var compilation = context.Compilation
|
var compilation = context.Compilation
|
||||||
.AddSource(ref context, MapFromAttributeSource.Generate(options))
|
.AddSource(ref context, MapFromAttributeSource.Generate(options))
|
||||||
.AddSource(ref context, IgnorePropertyAttributeSource.Generate(options))
|
.AddSource(ref context, IgnorePropertyAttributeSource.Generate(options))
|
||||||
.AddSource(ref context, TypeConverterSource.Generate(options))
|
.AddSource(ref context, ITypeConverterSource.Generate(options))
|
||||||
.AddSource(ref context, MapTypeConverterAttributeSource.Generate(options));
|
.AddSource(ref context, MapTypeConverterAttributeSource.Generate(options));
|
||||||
|
|
||||||
if (context.SyntaxReceiver is MapToSyntaxReceiver receiver && receiver.CandidateClasses.Any())
|
if (context.SyntaxReceiver is MapToSyntaxReceiver receiver && receiver.CandidateClasses.Any())
|
||||||
|
|
|
@ -24,8 +24,8 @@ namespace MapTo
|
||||||
MapTypeConverterAttributeTypeSymbol = compilation.GetTypeByMetadataName(MapTypeConverterAttributeSource.FullyQualifiedName)
|
MapTypeConverterAttributeTypeSymbol = compilation.GetTypeByMetadataName(MapTypeConverterAttributeSource.FullyQualifiedName)
|
||||||
?? throw new TypeLoadException($"Unable to find '{MapTypeConverterAttributeSource.FullyQualifiedName}' type.");
|
?? throw new TypeLoadException($"Unable to find '{MapTypeConverterAttributeSource.FullyQualifiedName}' type.");
|
||||||
|
|
||||||
TypeConverterInterfaceTypeSymbol = compilation.GetTypeByMetadataName(TypeConverterSource.FullyQualifiedName)
|
TypeConverterInterfaceTypeSymbol = compilation.GetTypeByMetadataName(ITypeConverterSource.FullyQualifiedName)
|
||||||
?? throw new TypeLoadException($"Unable to find '{TypeConverterSource.FullyQualifiedName}' type.");
|
?? throw new TypeLoadException($"Unable to find '{ITypeConverterSource.FullyQualifiedName}' type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public INamedTypeSymbol MapTypeConverterAttributeTypeSymbol { get; }
|
public INamedTypeSymbol MapTypeConverterAttributeTypeSymbol { get; }
|
||||||
|
@ -114,9 +114,17 @@ namespace MapTo
|
||||||
}
|
}
|
||||||
|
|
||||||
string? converterFullyQualifiedName = null;
|
string? converterFullyQualifiedName = null;
|
||||||
|
var converterParameters = new List<string>();
|
||||||
if (!SymbolEqualityComparer.Default.Equals(property.Type, sourceProperty.Type) && !context.Compilation.HasImplicitConversion(sourceProperty.Type, property.Type))
|
if (!SymbolEqualityComparer.Default.Equals(property.Type, sourceProperty.Type) && !context.Compilation.HasImplicitConversion(sourceProperty.Type, property.Type))
|
||||||
{
|
{
|
||||||
var converterTypeSymbol = property.GetAttribute(context.MapTypeConverterAttributeTypeSymbol)?.ConstructorArguments.First().Value as INamedTypeSymbol;
|
var typeConverterAttribute = property.GetAttribute(context.MapTypeConverterAttributeTypeSymbol);
|
||||||
|
if (typeConverterAttribute is null)
|
||||||
|
{
|
||||||
|
context.ReportDiagnostic(DiagnosticProvider.NoMatchingPropertyTypeFoundError(property));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var converterTypeSymbol = typeConverterAttribute.ConstructorArguments.First().Value as INamedTypeSymbol;
|
||||||
if (converterTypeSymbol is null)
|
if (converterTypeSymbol is null)
|
||||||
{
|
{
|
||||||
context.ReportDiagnostic(DiagnosticProvider.NoMatchingPropertyTypeFoundError(property));
|
context.ReportDiagnostic(DiagnosticProvider.NoMatchingPropertyTypeFoundError(property));
|
||||||
|
@ -136,9 +144,15 @@ namespace MapTo
|
||||||
}
|
}
|
||||||
|
|
||||||
converterFullyQualifiedName = converterTypeSymbol.ToDisplayString();
|
converterFullyQualifiedName = converterTypeSymbol.ToDisplayString();
|
||||||
|
|
||||||
|
var converterParameter = typeConverterAttribute.ConstructorArguments.Skip(1).FirstOrDefault();
|
||||||
|
if (!converterParameter.IsNull )
|
||||||
|
{
|
||||||
|
converterParameters.AddRange(converterParameter.Values.Where(v => v.Value is not null).Select(v => v.Value!.ToSourceCodeString()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mappedProperties.Add(new MappedProperty(property.Name, converterFullyQualifiedName));
|
mappedProperties.Add(new MappedProperty(property.Name, converterFullyQualifiedName, converterParameters.ToImmutableArray()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return mappedProperties.ToImmutableArray();
|
return mappedProperties.ToImmutableArray();
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace MapTo
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal record MappedProperty(string Name, string? TypeConverter);
|
internal record MappedProperty(string Name, string? TypeConverter, ImmutableArray<string> TypeConverterParameters);
|
||||||
|
|
||||||
internal record MappingModel (
|
internal record MappingModel (
|
||||||
SourceGenerationOptions Options,
|
SourceGenerationOptions Options,
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
using Microsoft.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
using static MapTo.Sources.Constants;
|
using static MapTo.Sources.Constants;
|
||||||
|
|
||||||
namespace MapTo.Sources
|
namespace MapTo.Sources
|
||||||
{
|
{
|
||||||
internal class TypeConverterSource
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
|
internal static class ITypeConverterSource
|
||||||
{
|
{
|
||||||
internal const string InterfaceName = "ITypeConverter";
|
internal const string InterfaceName = "ITypeConverter";
|
||||||
internal const string FullyQualifiedName = RootNamespace + "." + InterfaceName + "`2";
|
internal const string FullyQualifiedName = RootNamespace + "." + InterfaceName + "`2";
|
||||||
|
@ -34,14 +36,15 @@ namespace MapTo.Sources
|
||||||
{
|
{
|
||||||
builder
|
builder
|
||||||
.WriteLine("/// <summary>")
|
.WriteLine("/// <summary>")
|
||||||
.WriteLine("/// Converts the value of <paramref name=\"value\"/> object to <typeparamref name=\"TDestination\"/>.")
|
.WriteLine("/// Converts the value of <paramref name=\"source\"/> object to <typeparamref name=\"TDestination\"/>.")
|
||||||
.WriteLine("/// </summary>")
|
.WriteLine("/// </summary>")
|
||||||
.WriteLine("/// <param name=\"value\">The object to convert.</param>")
|
.WriteLine("/// <param name=\"source\">The <see cref=\"TSource\"/> to convert.</param>")
|
||||||
|
.WriteLine($"/// <param name=\"converterParameters\">The parameter list passed to the <see cref=\"{MapTypeConverterAttributeSource.AttributeClassName}\"/></param>")
|
||||||
.WriteLine("/// <returns><typeparamref name=\"TDestination\"/> object.</returns>");
|
.WriteLine("/// <returns><typeparamref name=\"TDestination\"/> object.</returns>");
|
||||||
}
|
}
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.WriteLine("TDestination Convert(TSource source);")
|
.WriteLine("TDestination Convert(TSource source, object[] converterParameters);")
|
||||||
.WriteClosingBracket()
|
.WriteClosingBracket()
|
||||||
.WriteClosingBracket();
|
.WriteClosingBracket();
|
||||||
|
|
|
@ -66,13 +66,17 @@ namespace MapTo.Sources
|
||||||
|
|
||||||
foreach (var property in model.MappedProperties)
|
foreach (var property in model.MappedProperties)
|
||||||
{
|
{
|
||||||
if (property.TypeConverter is not null)
|
if (property.TypeConverter is null)
|
||||||
{
|
{
|
||||||
builder.WriteLine($"{property.Name} = new {property.TypeConverter}().Convert({sourceClassParameterName}.{property.Name});");
|
builder.WriteLine($"{property.Name} = {sourceClassParameterName}.{property.Name};");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
builder.WriteLine($"{property.Name} = {sourceClassParameterName}.{property.Name};");
|
var parameters = property.TypeConverterParameters.IsEmpty
|
||||||
|
? "null"
|
||||||
|
: $"new object[] {{ {string.Join(", ", property.TypeConverterParameters)} }}";
|
||||||
|
|
||||||
|
builder.WriteLine($"{property.Name} = new {property.TypeConverter}().Convert({sourceClassParameterName}.{property.Name}, {parameters});");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ namespace MapTo.Sources
|
||||||
internal const string AttributeClassName = AttributeName + "Attribute";
|
internal const string AttributeClassName = AttributeName + "Attribute";
|
||||||
internal const string FullyQualifiedName = RootNamespace + "." + AttributeClassName;
|
internal const string FullyQualifiedName = RootNamespace + "." + AttributeClassName;
|
||||||
internal const string ConverterPropertyName = "Converter";
|
internal const string ConverterPropertyName = "Converter";
|
||||||
|
internal const string ConverterParametersPropertyName = "ConverterParameters";
|
||||||
|
|
||||||
internal static SourceCode Generate(SourceGenerationOptions options)
|
internal static SourceCode Generate(SourceGenerationOptions options)
|
||||||
{
|
{
|
||||||
|
@ -37,13 +38,16 @@ namespace MapTo.Sources
|
||||||
builder
|
builder
|
||||||
.WriteLine("/// <summary>")
|
.WriteLine("/// <summary>")
|
||||||
.WriteLine($"/// Initializes a new instance of <see cref=\"{AttributeClassName}\"/>.")
|
.WriteLine($"/// Initializes a new instance of <see cref=\"{AttributeClassName}\"/>.")
|
||||||
.WriteLine("/// </summary>");
|
.WriteLine("/// </summary>")
|
||||||
|
.WriteLine($"/// <param name=\"converter\">The <see cref=\"{ITypeConverterSource.InterfaceName}{{TSource,TDestination}}\" /> to be used to convert the source type.</param>")
|
||||||
|
.WriteLine("/// <param name=\"converterParameters\">The parameter list to pass to the <paramref name=\"converter\"/> during the type conversion.</param>");
|
||||||
}
|
}
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.WriteLine($"public {AttributeClassName}(Type converter)")
|
.WriteLine($"public {AttributeClassName}(Type converter, object[] converterParameters = null)")
|
||||||
.WriteOpeningBracket()
|
.WriteOpeningBracket()
|
||||||
.WriteLine($"{ConverterPropertyName} = converter;")
|
.WriteLine($"{ConverterPropertyName} = converter;")
|
||||||
|
.WriteLine($"{ConverterParametersPropertyName} = converterParameters;")
|
||||||
.WriteClosingBracket()
|
.WriteClosingBracket()
|
||||||
.WriteLine();
|
.WriteLine();
|
||||||
|
|
||||||
|
@ -51,12 +55,24 @@ namespace MapTo.Sources
|
||||||
{
|
{
|
||||||
builder
|
builder
|
||||||
.WriteLine("/// <summary>")
|
.WriteLine("/// <summary>")
|
||||||
.WriteLine($"/// Gets or sets the <see cref=\"{TypeConverterSource.InterfaceName}{{TSource,TDestination}}\" /> to be used to convert the source type.")
|
.WriteLine($"/// Gets or sets the <see cref=\"{ITypeConverterSource.InterfaceName}{{TSource,TDestination}}\" /> to be used to convert the source type.")
|
||||||
.WriteLine("/// </summary>");
|
.WriteLine("/// </summary>");
|
||||||
}
|
}
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.WriteLine($"public Type {ConverterPropertyName} {{ get; }}")
|
.WriteLine($"public Type {ConverterPropertyName} {{ get; }}")
|
||||||
|
.WriteLine();
|
||||||
|
|
||||||
|
if (options.GenerateXmlDocument)
|
||||||
|
{
|
||||||
|
builder
|
||||||
|
.WriteLine("/// <summary>")
|
||||||
|
.WriteLine($"/// Gets the parameter list to pass to the <see cref=\"{ConverterPropertyName}\"/> during the type conversion.")
|
||||||
|
.WriteLine("/// </summary>");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder
|
||||||
|
.WriteLine($"public object[] {ConverterParametersPropertyName} {{ get; }}")
|
||||||
.WriteClosingBracket()
|
.WriteClosingBracket()
|
||||||
.WriteClosingBracket();
|
.WriteClosingBracket();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using MapTo.Extensions;
|
using MapTo.Extensions;
|
||||||
|
@ -10,7 +9,6 @@ using MapTo.Tests.Infrastructure;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.CSharp;
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
using Microsoft.CodeAnalysis.Text;
|
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using static MapTo.Extensions.GeneratorExecutionContextExtensions;
|
using static MapTo.Extensions.GeneratorExecutionContextExtensions;
|
||||||
|
@ -65,7 +63,7 @@ namespace MapTo
|
||||||
builder.AppendLine("// Test source code.");
|
builder.AppendLine("// Test source code.");
|
||||||
builder.AppendLine("//");
|
builder.AppendLine("//");
|
||||||
builder.AppendLine();
|
builder.AppendLine();
|
||||||
|
|
||||||
if (options.UseMapToNamespace)
|
if (options.UseMapToNamespace)
|
||||||
{
|
{
|
||||||
builder.AppendFormat("using {0};", Constants.RootNamespace).AppendLine();
|
builder.AppendFormat("using {0};", Constants.RootNamespace).AppendLine();
|
||||||
|
@ -91,7 +89,7 @@ namespace MapTo
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.PadLeft(Indent1)
|
.PadLeft(Indent1)
|
||||||
.AppendLine(options.UseMapToNamespace ? "[MapFrom(typeof(Baz))]": "[MapTo.MapFrom(typeof(Baz))]")
|
.AppendLine(options.UseMapToNamespace ? "[MapFrom(typeof(Baz))]" : "[MapTo.MapFrom(typeof(Baz))]")
|
||||||
.PadLeft(Indent1).Append("public partial class Foo")
|
.PadLeft(Indent1).Append("public partial class Foo")
|
||||||
.AppendOpeningBracket(Indent1);
|
.AppendOpeningBracket(Indent1);
|
||||||
|
|
||||||
|
@ -182,10 +180,10 @@ namespace MapTo
|
||||||
.PadLeft(Indent2).AppendLine("public string Prop4 { get; set; }");
|
.PadLeft(Indent2).AppendLine("public string Prop4 { get; set; }");
|
||||||
},
|
},
|
||||||
SourcePropertyBuilder: builder => builder.PadLeft(Indent2).AppendLine("public int Prop4 { get; set; }")));
|
SourcePropertyBuilder: builder => builder.PadLeft(Indent2).AppendLine("public int Prop4 { get; set; }")));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var expectedError = DiagnosticProvider.NoMatchingPropertyTypeFoundError(GetSourcePropertySymbol("Prop4", compilation));
|
var expectedError = DiagnosticProvider.NoMatchingPropertyTypeFoundError(GetSourcePropertySymbol("Prop4", compilation));
|
||||||
|
|
||||||
|
@ -221,7 +219,7 @@ namespace MapTo
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
diagnostics.ShouldBeSuccessful();
|
diagnostics.ShouldBeSuccessful();
|
||||||
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult);
|
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult);
|
||||||
|
@ -332,7 +330,7 @@ namespace Test
|
||||||
// Assert
|
// Assert
|
||||||
var fooType = compilation.GetTypeByMetadataName("Test.Foo");
|
var fooType = compilation.GetTypeByMetadataName("Test.Foo");
|
||||||
fooType.ShouldNotBeNull();
|
fooType.ShouldNotBeNull();
|
||||||
|
|
||||||
var bazType = compilation.GetTypeByMetadataName("Test.Baz");
|
var bazType = compilation.GetTypeByMetadataName("Test.Baz");
|
||||||
bazType.ShouldNotBeNull();
|
bazType.ShouldNotBeNull();
|
||||||
|
|
||||||
|
@ -384,7 +382,7 @@ namespace Test
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
const string source = "";
|
const string source = "";
|
||||||
var expectedTypes = new[] { IgnorePropertyAttributeSource.AttributeName, MapFromAttributeSource.AttributeName, TypeConverterSource.InterfaceName };
|
var expectedTypes = new[] { IgnorePropertyAttributeSource.AttributeName, MapFromAttributeSource.AttributeName, ITypeConverterSource.InterfaceName };
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source);
|
||||||
|
@ -490,7 +488,7 @@ namespace Test
|
||||||
diagnostics.ShouldBeSuccessful();
|
diagnostics.ShouldBeSuccessful();
|
||||||
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult.Trim());
|
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult.Trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void VerifyTypeConverterInterface()
|
public void VerifyTypeConverterInterface()
|
||||||
{
|
{
|
||||||
|
@ -503,7 +501,7 @@ namespace MapTo
|
||||||
{{
|
{{
|
||||||
public interface ITypeConverter<in TSource, out TDestination>
|
public interface ITypeConverter<in TSource, out TDestination>
|
||||||
{{
|
{{
|
||||||
TDestination Convert(TSource source);
|
TDestination Convert(TSource source, object[] converterParameters);
|
||||||
}}
|
}}
|
||||||
}}
|
}}
|
||||||
".Trim();
|
".Trim();
|
||||||
|
@ -513,9 +511,9 @@ namespace MapTo
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
diagnostics.ShouldBeSuccessful();
|
diagnostics.ShouldBeSuccessful();
|
||||||
compilation.SyntaxTrees.ShouldContainSource(TypeConverterSource.InterfaceName, expectedInterface);
|
compilation.SyntaxTrees.ShouldContainSource(ITypeConverterSource.InterfaceName, expectedInterface);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void VerifyMapTypeConverterAttribute()
|
public void VerifyMapTypeConverterAttribute()
|
||||||
{
|
{
|
||||||
|
@ -530,12 +528,15 @@ namespace MapTo
|
||||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
|
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
|
||||||
public sealed class MapTypeConverterAttribute : Attribute
|
public sealed class MapTypeConverterAttribute : Attribute
|
||||||
{{
|
{{
|
||||||
public MapTypeConverterAttribute(Type converter)
|
public MapTypeConverterAttribute(Type converter, object[] converterParameters = null)
|
||||||
{{
|
{{
|
||||||
Converter = converter;
|
Converter = converter;
|
||||||
|
ConverterParameters = converterParameters;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public Type Converter {{ get; }}
|
public Type Converter {{ get; }}
|
||||||
|
|
||||||
|
public object[] ConverterParameters {{ get; }}
|
||||||
}}
|
}}
|
||||||
}}
|
}}
|
||||||
".Trim();
|
".Trim();
|
||||||
|
@ -547,7 +548,7 @@ namespace MapTo
|
||||||
diagnostics.ShouldBeSuccessful();
|
diagnostics.ShouldBeSuccessful();
|
||||||
compilation.SyntaxTrees.ShouldContainSource(MapTypeConverterAttributeSource.AttributeName, expectedInterface);
|
compilation.SyntaxTrees.ShouldContainSource(MapTypeConverterAttributeSource.AttributeName, expectedInterface);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void When_FoundMatchingPropertyNameWithDifferentImplicitlyConvertibleType_Should_GenerateTheProperty()
|
public void When_FoundMatchingPropertyNameWithDifferentImplicitlyConvertibleType_Should_GenerateTheProperty()
|
||||||
{
|
{
|
||||||
|
@ -582,7 +583,7 @@ namespace MapTo
|
||||||
diagnostics.ShouldBeSuccessful();
|
diagnostics.ShouldBeSuccessful();
|
||||||
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult);
|
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void When_FoundMatchingPropertyNameWithIncorrectConverterType_ShouldReportError()
|
public void When_FoundMatchingPropertyNameWithIncorrectConverterType_ShouldReportError()
|
||||||
{
|
{
|
||||||
|
@ -606,19 +607,19 @@ namespace Test
|
||||||
|
|
||||||
public class Prop4Converter: ITypeConverter<string, int>
|
public class Prop4Converter: ITypeConverter<string, int>
|
||||||
{
|
{
|
||||||
public int Convert(string source) => int.Parse(source);
|
public int Convert(string source, object[] converterParameters) => int.Parse(source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
";
|
";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var expectedError = DiagnosticProvider.InvalidTypeConverterGenericTypesError(GetSourcePropertySymbol("Prop4", compilation), GetSourcePropertySymbol("Prop4", compilation, "Baz"));
|
var expectedError = DiagnosticProvider.InvalidTypeConverterGenericTypesError(GetSourcePropertySymbol("Prop4", compilation), GetSourcePropertySymbol("Prop4", compilation, "Baz"));
|
||||||
diagnostics.ShouldBeUnsuccessful(expectedError);
|
diagnostics.ShouldBeUnsuccessful(expectedError);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void When_FoundMatchingPropertyNameWithConverterType_ShouldUseTheConverterToAssignProperties()
|
public void When_FoundMatchingPropertyNameWithConverterType_ShouldUseTheConverterToAssignProperties()
|
||||||
{
|
{
|
||||||
|
@ -640,16 +641,52 @@ namespace Test
|
||||||
|
|
||||||
public class Prop4Converter: ITypeConverter<string, long>
|
public class Prop4Converter: ITypeConverter<string, long>
|
||||||
{
|
{
|
||||||
public long Convert(string source) => long.Parse(source);
|
public long Convert(string source, object[] converterParameters) => long.Parse(source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
";
|
";
|
||||||
|
|
||||||
const string expectedSyntax = "Prop4 = new Test.Prop4Converter().Convert(baz.Prop4);";
|
const string expectedSyntax = "Prop4 = new Test.Prop4Converter().Convert(baz.Prop4, null);";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
diagnostics.ShouldBeSuccessful();
|
||||||
|
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedSyntax);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void When_FoundMatchingPropertyNameWithConverterType_ShouldUseTheConverterAndItsParametersToAssignProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var source = GetSourceText(new SourceGeneratorOptions(
|
||||||
|
true,
|
||||||
|
PropertyBuilder: builder =>
|
||||||
|
{
|
||||||
|
builder
|
||||||
|
.PadLeft(Indent2).AppendLine("[MapTypeConverter(typeof(Prop4Converter), new object[]{\"G\", 'C', 10})]")
|
||||||
|
.PadLeft(Indent2).AppendLine("public string Prop4 { get; set; }");
|
||||||
|
},
|
||||||
|
SourcePropertyBuilder: builder => builder.PadLeft(Indent2).AppendLine("public long Prop4 { get; set; }")));
|
||||||
|
|
||||||
|
source += @"
|
||||||
|
namespace Test
|
||||||
|
{
|
||||||
|
using MapTo;
|
||||||
|
|
||||||
|
public class Prop4Converter: ITypeConverter<long, string>
|
||||||
|
{
|
||||||
|
public string Convert(long source, object[] converterParameters) => source.ToString(converterParameters[0] as string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
const string expectedSyntax = "Prop4 = new Test.Prop4Converter().Convert(baz.Prop4, new object[] { \"G\", 'C', 10 });";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
diagnostics.ShouldBeSuccessful();
|
diagnostics.ShouldBeSuccessful();
|
||||||
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedSyntax);
|
compilation.SyntaxTrees.Last().ToString().ShouldContain(expectedSyntax);
|
||||||
|
@ -665,12 +702,12 @@ namespace Test
|
||||||
.OfType<PropertyDeclarationSyntax>()
|
.OfType<PropertyDeclarationSyntax>()
|
||||||
.Single(p => p.Identifier.ValueText == targetPropertyName);
|
.Single(p => p.Identifier.ValueText == targetPropertyName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IPropertySymbol GetSourcePropertySymbol(string propertyName, Compilation compilation, string targetClass = "Foo")
|
private static IPropertySymbol GetSourcePropertySymbol(string propertyName, Compilation compilation, string targetClass = "Foo")
|
||||||
{
|
{
|
||||||
var syntaxTree = compilation.SyntaxTrees.First();
|
var syntaxTree = compilation.SyntaxTrees.First();
|
||||||
var propSyntax = GetPropertyDeclarationSyntax(syntaxTree, propertyName, targetClass);
|
var propSyntax = GetPropertyDeclarationSyntax(syntaxTree, propertyName, targetClass);
|
||||||
|
|
||||||
var semanticModel = compilation.GetSemanticModel(syntaxTree);
|
var semanticModel = compilation.GetSemanticModel(syntaxTree);
|
||||||
return semanticModel.GetDeclaredSymbol(propSyntax);
|
return semanticModel.GetDeclaredSymbol(propSyntax);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace TestConsoleApp.ViewModels
|
||||||
private class LastNameConverter : ITypeConverter<long, string>
|
private class LastNameConverter : ITypeConverter<long, string>
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string Convert(long source) => $"{source} :: With Type Converter";
|
public string Convert(long source, object[] converterParameters) => $"{source} :: With Type Converter";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue