This commit is contained in:
Wvader 2021-12-24 17:06:06 +00:00
parent 60ccbd53ca
commit ffc3dc7729
76 changed files with 6398 additions and 6089 deletions

View File

@ -18,7 +18,7 @@ namespace MapTo
return typeSymbol return typeSymbol
.GetAllMembers() .GetAllMembers()
.OfType<IFieldSymbol>() .OfType<IFieldSymbol>()
.Where(p => !p.HasAttribute(IgnorePropertyAttributeTypeSymbol)) .Where(p => !p.HasAttribute(IgnoreMemberAttributeTypeSymbol))
.Select(property => MapField(sourceTypeSymbol, sourceProperties, property)) .Select(property => MapField(sourceTypeSymbol, sourceProperties, property))
.Where(mappedProperty => mappedProperty is not null) .Where(mappedProperty => mappedProperty is not null)
.ToImmutableArray()!; .ToImmutableArray()!;
@ -31,7 +31,7 @@ namespace MapTo
return typeSymbol return typeSymbol
.GetAllMembers() .GetAllMembers()
.OfType<IPropertySymbol>() .OfType<IPropertySymbol>()
.Where(p => !p.HasAttribute(IgnorePropertyAttributeTypeSymbol)) .Where(p => !p.HasAttribute(IgnoreMemberAttributeTypeSymbol))
.Select(property => MapProperty(sourceTypeSymbol, sourceProperties, property)) .Select(property => MapProperty(sourceTypeSymbol, sourceProperties, property))
.Where(mappedProperty => mappedProperty is not null) .Where(mappedProperty => mappedProperty is not null)
.ToImmutableArray()!; .ToImmutableArray()!;
@ -44,7 +44,7 @@ namespace MapTo
return sourceTypeSymbol return sourceTypeSymbol
.GetAllMembers() .GetAllMembers()
.OfType<IFieldSymbol>() .OfType<IFieldSymbol>()
.Where(p => !p.HasAttribute(IgnorePropertyAttributeTypeSymbol)) .Where(p => !p.HasAttribute(IgnoreMemberAttributeTypeSymbol))
.Select(property => MapFieldSimple(typeSymbol, property)) .Select(property => MapFieldSimple(typeSymbol, property))
.Where(mappedProperty => mappedProperty is not null) .Where(mappedProperty => mappedProperty is not null)
.ToImmutableArray()!; .ToImmutableArray()!;
@ -57,7 +57,7 @@ namespace MapTo
return sourceTypeSymbol return sourceTypeSymbol
.GetAllMembers() .GetAllMembers()
.OfType<IPropertySymbol>() .OfType<IPropertySymbol>()
.Where(p => !p.HasAttribute(IgnorePropertyAttributeTypeSymbol)) .Where(p => !p.HasAttribute(IgnoreMemberAttributeTypeSymbol))
.Select(property => MapPropertySimple(typeSymbol, property)) .Select(property => MapPropertySimple(typeSymbol, property))
.Where(mappedProperty => mappedProperty is not null) .Where(mappedProperty => mappedProperty is not null)
.ToImmutableArray()!; .ToImmutableArray()!;

View File

@ -25,7 +25,7 @@ namespace MapTo
Create($"{ErrorId}030", location, $"No matching properties found between '{classType.ToDisplayString()}' and '{sourceType.ToDisplayString()}' types."); Create($"{ErrorId}030", location, $"No matching properties found between '{classType.ToDisplayString()}' and '{sourceType.ToDisplayString()}' types.");
internal static Diagnostic NoMatchingPropertyTypeFoundError(ISymbol property) => internal static Diagnostic NoMatchingPropertyTypeFoundError(ISymbol property) =>
Create($"{ErrorId}031", property.Locations.FirstOrDefault(), $"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(), $"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 '{IgnoreMemberAttributeSource.FullyQualifiedName}'.");
internal static Diagnostic InvalidTypeConverterGenericTypesError(ISymbol property, IPropertySymbol sourceProperty) => internal static Diagnostic InvalidTypeConverterGenericTypesError(ISymbol property, IPropertySymbol sourceProperty) =>
Create($"{ErrorId}032", property.Locations.FirstOrDefault(), $"Cannot map '{property.ToDisplayString()}' property because the annotated converter does not implement '{RootNamespace}.{ITypeConverterSource.InterfaceName}<{sourceProperty.Type.ToDisplayString()}, {property.GetTypeSymbol()?.ToDisplayString()}>'."); Create($"{ErrorId}032", property.Locations.FirstOrDefault(), $"Cannot map '{property.ToDisplayString()}' property because the annotated converter does not implement '{RootNamespace}.{ITypeConverterSource.InterfaceName}<{sourceProperty.Type.ToDisplayString()}, {property.GetTypeSymbol()?.ToDisplayString()}>'.");

View File

@ -1,4 +1,5 @@
using MapTo.Sources; using MapTo.Sources;
using Microsoft.CodeAnalysis;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
@ -12,6 +13,21 @@ namespace MapTo.Extensions
return builder.WriteLine($"// {comment}"); return builder.WriteLine($"// {comment}");
} }
internal static SourceBuilder WriteCommentArray(this SourceBuilder builder, IEnumerable<object> enumerable, string name = "")
{
builder.WriteComment($"Printing Array: {name}");
foreach (var o in enumerable)
{
if (o != null)
{
builder.WriteComment($" {o.ToString()}");
}
}
builder.WriteComment($"End printing Array: {name}");
return builder;
}
internal static SourceBuilder WriteModelInfo(this SourceBuilder builder, MappingModel model) internal static SourceBuilder WriteModelInfo(this SourceBuilder builder, MappingModel model)
{ {
return builder return builder
@ -32,6 +48,16 @@ namespace MapTo.Extensions
{ {
foreach (var item in mappedProperties) foreach (var item in mappedProperties)
{ {
string str = "";
if (item.NamedTypeSymbol != null)
foreach (var named in item.NamedTypeSymbol?.TypeArguments)
{
str += $"typeToString: {named.ToString()} ";
bool? containedTypeIsJsonEXtension = named?.HasAttribute(MappingContext.JsonExtensionAttributeSymbol);
str += $"typeArgumentTypeIsJsonExtensioN: {containedTypeIsJsonEXtension.ToString()}";
}
builder .WriteComment($" Name {item.Name}") builder .WriteComment($" Name {item.Name}")
.WriteComment($" Type {item.Type}") .WriteComment($" Type {item.Type}")
.WriteComment($" MappedSourcePropertyTypeName {item.MappedSourcePropertyTypeName}") .WriteComment($" MappedSourcePropertyTypeName {item.MappedSourcePropertyTypeName}")
@ -41,6 +67,10 @@ namespace MapTo.Extensions
.WriteComment($" SourcePropertyName {item.SourcePropertyName}") .WriteComment($" SourcePropertyName {item.SourcePropertyName}")
.WriteComment($" TypeSymbol {item.FullyQualifiedType.ToString()}") .WriteComment($" TypeSymbol {item.FullyQualifiedType.ToString()}")
.WriteComment($" isReadOnly {item.isReadOnly.ToString()}") .WriteComment($" isReadOnly {item.isReadOnly.ToString()}")
.WriteComment($" isEnumerable {item.isEnumerable.ToString()}")
.WriteComment($" INamedTypeSymbol {item.NamedTypeSymbol?.ToString()}")
.WriteComment($" INamedTypeSymbolTypeArguments {str}")
.WriteLine(); .WriteLine();
} }

View File

@ -4,6 +4,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis;
namespace MapTo.Extensions namespace MapTo.Extensions
{ {
@ -23,7 +25,7 @@ namespace MapTo.Extensions
.WriteLine($"namespace {model.Namespace}") .WriteLine($"namespace {model.Namespace}")
.WriteOpeningBracket(); .WriteOpeningBracket();
if(writeDebugInfo) if (writeDebugInfo)
builder builder
.WriteModelInfo(model) .WriteModelInfo(model)
.WriteLine() .WriteLine()
@ -49,7 +51,9 @@ namespace MapTo.Extensions
// Class body // Class body
.GeneratePublicConstructor(model); .GeneratePublicConstructor(model);
if (model.IsJsonExtension) builder.WriteToJsonMethod(model);
if (model.IsTypeUpdatable && model.TypeProperties.GetWritableMappedProperties().Length > 0) builder.GenerateUpdateMethod(model); if (model.IsTypeUpdatable && model.TypeProperties.GetWritableMappedProperties().Length > 0) builder.GenerateUpdateMethod(model);
if (model.IsTypeUpdatable && model.TypeFields.GetWritableMappedProperties().Length > 0) builder.GenerateUpdateMethod(model);
builder builder
.WriteLine() .WriteLine()
@ -105,9 +109,10 @@ namespace MapTo.Extensions
return builder.WriteClosingBracket(); return builder.WriteClosingBracket();
} }
private static bool IsMappedProperty(this System.Collections.Immutable.ImmutableArray<MappedMember> properties, MappedMember property) { private static bool IsMappedProperty(this System.Collections.Immutable.ImmutableArray<MappedMember> properties, MappedMember property)
{
foreach(var prop in properties) foreach (var prop in properties)
{ {
if (prop.Name == property.Name) return true; if (prop.Name == property.Name) return true;
} }
@ -115,6 +120,110 @@ namespace MapTo.Extensions
return false; return false;
} }
private static SourceBuilder WriteToJsonMethod(this SourceBuilder builder, MappingModel model)
{
builder
.WriteLine($"public string ToJson()")
.WriteOpeningBracket()
.WriteLine("var stringBuilder = new System.Text.StringBuilder();")
.WriteLine(GetStringBuilderAppendNoInterpolation("{"));
foreach (var property in model.TypeProperties)
{
if (!property.isEnumerable)
HandlePropertyEnumerable(builder, property);
else
{
builder = WriteJsonField(builder, property);
}
}
foreach (var property in model.TypeFields)
{
if (!property.isEnumerable)
HandleFieldEnumerable(builder, property);
else
{
builder.WriteLine(GetStringBuilderAppend($"\\\"{property.Name.ToCamelCase()}\\\" : [{GetJsonArrayValue(property, ref builder)}],"));
}
}
builder.WriteLine(GetStringBuilderAppendNoInterpolation("}"));
builder.WriteLine("return stringBuilder.ToString();");
builder.WriteClosingBracket();
return builder;
}
private static SourceBuilder WriteJsonField(SourceBuilder builder, MappedMember property)
{
builder.WriteLine(
GetStringBuilderAppend(
$"\\\"{property.Name.ToCamelCase()}\\\" : [{GetJsonArrayValue(property, ref builder)}],"));
return builder;
}
private static void HandleEnumerable(SourceBuilder builder, MappedMember property)
{
var symbol = property.ActualSymbol as IPropertySymbol;
builder.WriteCommentArray(symbol.Parameters, nameof(symbol.Parameters));
builder.WriteCommentArray(symbol.TypeCustomModifiers, nameof(symbol.TypeCustomModifiers));
builder.WriteComment($"Is enumerable {(property.ActualSymbol as IPropertySymbol).Parameters}");
builder.WriteLine(
GetStringBuilderAppend($"\\\"{property.Name.ToCamelCase()}\\\" : {GetJsonValue(property, builder)},"));
}
private static void HandleFieldEnumerable(SourceBuilder builder, MappedMember property)
{
HandleEnumerable(builder, property);
}
private static void HandlePropertyEnumerable(SourceBuilder builder, MappedMember property)
{
HandleEnumerable(builder, property);
}
private static string GetJsonArrayValue(MappedMember member, ref SourceBuilder builder)
{
if (member.isEnumerable)
{
// get underlying type (check if is a json extension)
builder.WriteLine("var arrStrBuilder = new StringBuilder();");
foreach (var named in member.NamedTypeSymbol?.TypeArguments!)
{
bool? containedTypeIsJsonEXtension = named?.HasAttribute(MappingContext.JsonExtensionAttributeSymbol);
if (!containedTypeIsJsonEXtension.HasValue) continue;
builder.WriteLine($"foreach (var v in {member.SourcePropertyName.ToString()})");
builder.WriteOpeningBracket();
builder.WriteLine("arrStrBuilder.Append(v.ToJson());");
builder.WriteLine("arrStrBuilder.Append(\", \");");
builder.WriteClosingBracket();
}
builder.WriteLine("arrStrBuilder.Remove(arrStrBuilder.Length -1, 1);");
}
return "{arrStrBuilder.ToString()}";
}
private static string GetJsonValue(MappedMember member, SourceBuilder builder)
{
if (member.FullyQualifiedType == "string") return $"\\\"{{{member.SourcePropertyName}}}\\\"";
if (member.FullyQualifiedType is "int" or "double" or "float" or "long") return $"{{{member.SourcePropertyName}}}";
return "";
}
private static string GetStringBuilderAppend(string stringToAppend)
{
return $"stringBuilder.Append($\"{stringToAppend}\");";
}
private static string GetStringBuilderAppendNoInterpolation(string stringToAppend)
{
return $"stringBuilder.Append(\"{stringToAppend}\");";
}
private static SourceBuilder WriteAssignmentMethod(this SourceBuilder builder, MappingModel model, System.Collections.Immutable.ImmutableArray<MappedMember>? otherProperties, private static SourceBuilder WriteAssignmentMethod(this SourceBuilder builder, MappingModel model, System.Collections.Immutable.ImmutableArray<MappedMember>? otherProperties,
string? sourceClassParameterName, string mappingContextParameterName, bool fromUpdate) string? sourceClassParameterName, string mappingContextParameterName, bool fromUpdate)
{ {
@ -123,7 +232,7 @@ namespace MapTo.Extensions
{ {
if (property.isReadOnly && fromUpdate) continue; if (property.isReadOnly && fromUpdate) continue;
builder.WriteLine( $"{property.Name} = {sourceClassParameterName}.{property.SourcePropertyName};"); builder.WriteLine($"{property.Name} = {sourceClassParameterName}.{property.SourcePropertyName};");
} }
@ -178,5 +287,17 @@ namespace MapTo.Extensions
.WriteLine("/// </summary>") .WriteLine("/// </summary>")
.WriteLine($"/// <param name=\"{sourceClassParameterName}\">The instance of <see cref=\"{model.SourceType}\"/> to use as source.</param>"); .WriteLine($"/// <param name=\"{sourceClassParameterName}\">The instance of <see cref=\"{model.SourceType}\"/> to use as source.</param>");
} }
private static SourceBuilder GenerateEnumerableJsonSourceTypeExtensionMethod(this SourceBuilder builder, MappingModel model)
{
var sourceClassParameterName = model.SourceTypeIdentifierName.ToCamelCase();
return builder
.WriteLineIf(model.Options.SupportNullableStaticAnalysis, $"[return: NotNullIfNotNull(\"{sourceClassParameterName}\")]")
.WriteLine($"{model.Options.GeneratedMethodsAccessModifier.ToLowercaseString()} static string ToJson(this IEnumerable<{model.SourceType}{model.Options.NullableReferenceSyntax}> {sourceClassParameterName}List)")
.WriteOpeningBracket()
.WriteLine($"return {sourceClassParameterName} == null ? null : new {model.TypeIdentifierName}({sourceClassParameterName});")
.WriteClosingBracket();
}
} }
} }

View File

@ -29,8 +29,9 @@ namespace MapTo
var compilation = context.Compilation var compilation = context.Compilation
.AddSource(ref context, UseUpdateAttributeSource.Generate(options)) .AddSource(ref context, UseUpdateAttributeSource.Generate(options))
.AddSource(ref context, JsonExtensionAttributeSource.Generate(options))
.AddSource(ref context, MapFromAttributeSource.Generate(options)) .AddSource(ref context, MapFromAttributeSource.Generate(options))
.AddSource(ref context, IgnorePropertyAttributeSource.Generate(options)) .AddSource(ref context, IgnoreMemberAttributeSource.Generate(options))
.AddSource(ref context, ITypeConverterSource.Generate(options)) .AddSource(ref context, ITypeConverterSource.Generate(options))
.AddSource(ref context, MapTypeConverterAttributeSource.Generate(options)) .AddSource(ref context, MapTypeConverterAttributeSource.Generate(options))
.AddSource(ref context, MapPropertyAttributeSource.Generate(options)) .AddSource(ref context, MapPropertyAttributeSource.Generate(options))

View File

@ -28,12 +28,13 @@ namespace MapTo
TypeSyntax = typeSyntax; TypeSyntax = typeSyntax;
Compilation = compilation; Compilation = compilation;
IgnorePropertyAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(IgnorePropertyAttributeSource.FullyQualifiedName); IgnoreMemberAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(IgnoreMemberAttributeSource.FullyQualifiedName);
MapTypeConverterAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(MapTypeConverterAttributeSource.FullyQualifiedName); MapTypeConverterAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(MapTypeConverterAttributeSource.FullyQualifiedName);
TypeConverterInterfaceTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(ITypeConverterSource.FullyQualifiedName); TypeConverterInterfaceTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(ITypeConverterSource.FullyQualifiedName);
MapPropertyAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(MapPropertyAttributeSource.FullyQualifiedName); MapPropertyAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(MapPropertyAttributeSource.FullyQualifiedName);
MapFromAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(MapFromAttributeSource.FullyQualifiedName); MapFromAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(MapFromAttributeSource.FullyQualifiedName);
UseUpdateAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(UseUpdateAttributeSource.FullyQualifiedName); UseUpdateAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(UseUpdateAttributeSource.FullyQualifiedName);
JsonExtensionAttributeSymbol = compilation.GetTypeByMetadataNameOrThrow(JsonExtensionAttributeSource.FullyQualifiedName);
MappingContextTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(MappingContextSource.FullyQualifiedName); MappingContextTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(MappingContextSource.FullyQualifiedName);
AddUsingIfRequired(sourceGenerationOptions.SupportNullableStaticAnalysis, "System.Diagnostics.CodeAnalysis"); AddUsingIfRequired(sourceGenerationOptions.SupportNullableStaticAnalysis, "System.Diagnostics.CodeAnalysis");
@ -45,12 +46,14 @@ namespace MapTo
protected Compilation Compilation { get; } protected Compilation Compilation { get; }
protected INamedTypeSymbol IgnorePropertyAttributeTypeSymbol { get; } protected INamedTypeSymbol IgnoreMemberAttributeTypeSymbol { get; }
protected INamedTypeSymbol MapFromAttributeTypeSymbol { get; } protected INamedTypeSymbol MapFromAttributeTypeSymbol { get; }
protected INamedTypeSymbol UseUpdateAttributeTypeSymbol { get; } protected INamedTypeSymbol UseUpdateAttributeTypeSymbol { get; }
public static INamedTypeSymbol JsonExtensionAttributeSymbol { get; set; }
protected INamedTypeSymbol MappingContextTypeSymbol { get; } protected INamedTypeSymbol MappingContextTypeSymbol { get; }
protected INamedTypeSymbol MapPropertyAttributeTypeSymbol { get; } protected INamedTypeSymbol MapPropertyAttributeTypeSymbol { get; }
@ -158,7 +161,10 @@ namespace MapTo
{ {
return TypeSyntax.GetAttribute("UseUpdate") != null; return TypeSyntax.GetAttribute("UseUpdate") != null;
} }
protected bool HasJsonExtension()
{
return TypeSyntax.GetAttribute("JsonExtension") != null;
}
protected virtual MappedMember? MapProperty(ISymbol sourceTypeSymbol, IReadOnlyCollection<IPropertySymbol> sourceProperties, ISymbol property) protected virtual MappedMember? MapProperty(ISymbol sourceTypeSymbol, IReadOnlyCollection<IPropertySymbol> sourceProperties, ISymbol property)
{ {
var sourceProperty = FindSourceProperty(sourceProperties, property); var sourceProperty = FindSourceProperty(sourceProperties, property);
@ -186,6 +192,9 @@ namespace MapTo
AddUsingIfRequired(enumerableTypeArgumentType); AddUsingIfRequired(enumerableTypeArgumentType);
AddUsingIfRequired(mappedSourcePropertyType); AddUsingIfRequired(mappedSourcePropertyType);
INamedTypeSymbol? namedType;
var isEnumerable = IsEnumerable(property, out namedType);
return new MappedMember( return new MappedMember(
property.Name, property.Name,
@ -196,6 +205,9 @@ namespace MapTo
sourceProperty.Name, sourceProperty.Name,
ToQualifiedDisplayName(mappedSourcePropertyType), ToQualifiedDisplayName(mappedSourcePropertyType),
ToQualifiedDisplayName(enumerableTypeArgumentType), ToQualifiedDisplayName(enumerableTypeArgumentType),
property,
namedType,
isEnumerable,
(property as IPropertySymbol).IsReadOnly); (property as IPropertySymbol).IsReadOnly);
; ;
} }
@ -232,6 +244,9 @@ namespace MapTo
AddUsingIfRequired(mappedSourcePropertyType); AddUsingIfRequired(mappedSourcePropertyType);
INamedTypeSymbol? namedType;
var isEnumerable = IsEnumerable(property, out namedType);
return new MappedMember( return new MappedMember(
property.Name, property.Name,
property.GetTypeSymbol().ToString(), property.GetTypeSymbol().ToString(),
@ -241,6 +256,9 @@ namespace MapTo
sourceProperty.Name, sourceProperty.Name,
ToQualifiedDisplayName(mappedSourcePropertyType), ToQualifiedDisplayName(mappedSourcePropertyType),
ToQualifiedDisplayName(enumerableTypeArgumentType), ToQualifiedDisplayName(enumerableTypeArgumentType),
property,
namedType,
isEnumerable,
(property as IFieldSymbol).IsReadOnly); (property as IFieldSymbol).IsReadOnly);
; ;
} }
@ -262,6 +280,8 @@ namespace MapTo
AddUsingIfRequired(enumerableTypeArgumentType); AddUsingIfRequired(enumerableTypeArgumentType);
AddUsingIfRequired(mappedSourcePropertyType); AddUsingIfRequired(mappedSourcePropertyType);
INamedTypeSymbol? namedType;
var isEnumerable = IsEnumerable(property, out namedType);
return new MappedMember( return new MappedMember(
property.Name, property.Name,
@ -272,6 +292,9 @@ namespace MapTo
property.Name, property.Name,
ToQualifiedDisplayName(mappedSourcePropertyType), ToQualifiedDisplayName(mappedSourcePropertyType),
ToQualifiedDisplayName(enumerableTypeArgumentType), ToQualifiedDisplayName(enumerableTypeArgumentType),
property,
namedType,
isEnumerable,
(property as IPropertySymbol).IsReadOnly); (property as IPropertySymbol).IsReadOnly);
; ;
} }
@ -299,6 +322,9 @@ namespace MapTo
AddUsingIfRequired(enumerableTypeArgumentType); AddUsingIfRequired(enumerableTypeArgumentType);
AddUsingIfRequired(mappedSourcePropertyType); AddUsingIfRequired(mappedSourcePropertyType);
INamedTypeSymbol? namedType;
var isEnumerable = IsEnumerable(property, out namedType);
return new MappedMember( return new MappedMember(
property.Name, property.Name,
@ -309,6 +335,9 @@ namespace MapTo
property.Name, property.Name,
ToQualifiedDisplayName(mappedSourcePropertyType), ToQualifiedDisplayName(mappedSourcePropertyType),
ToQualifiedDisplayName(enumerableTypeArgumentType), ToQualifiedDisplayName(enumerableTypeArgumentType),
property,
namedType,
isEnumerable,
(property as IFieldSymbol).IsReadOnly); (property as IFieldSymbol).IsReadOnly);
; ;
} }
@ -403,7 +432,27 @@ namespace MapTo
return Diagnostics.IsEmpty(); return Diagnostics.IsEmpty();
} }
protected bool IsEnumerable(ISymbol property, out INamedTypeSymbol? namedTypeSymbolResult)
{
if (!property.TryGetTypeSymbol(out var propertyType))
{
AddDiagnostic(DiagnosticsFactory.NoMatchingPropertyTypeFoundError(property));
namedTypeSymbolResult = null;
return false;
}
if (
propertyType is INamedTypeSymbol namedTypeSymbol &&
!propertyType.IsPrimitiveType() &&
(Compilation.IsGenericEnumerable(propertyType) || propertyType.AllInterfaces.Any(i => Compilation.IsGenericEnumerable(i))))
{
namedTypeSymbolResult = namedTypeSymbol;
return true;
}
namedTypeSymbolResult = null;
return false;
}
private static ImmutableArray<string> GetTypeConverterParameters(AttributeData typeConverterAttribute) private static ImmutableArray<string> GetTypeConverterParameters(AttributeData typeConverterAttribute)
{ {
var converterParameter = typeConverterAttribute.ConstructorArguments.Skip(1).FirstOrDefault(); var converterParameter = typeConverterAttribute.ConstructorArguments.Skip(1).FirstOrDefault();
@ -434,6 +483,7 @@ namespace MapTo
var sourceTypeIdentifierName = sourceTypeSymbol.Name; var sourceTypeIdentifierName = sourceTypeSymbol.Name;
var isTypeInheritFromMappedBaseClass = IsTypeInheritFromMappedBaseClass(semanticModel); var isTypeInheritFromMappedBaseClass = IsTypeInheritFromMappedBaseClass(semanticModel);
var isTypeUpdatable = IsTypeUpdatable(); var isTypeUpdatable = IsTypeUpdatable();
var hasJsonExtension = HasJsonExtension();
var shouldGenerateSecondaryConstructor = ShouldGenerateSecondaryConstructor(semanticModel, sourceTypeSymbol); var shouldGenerateSecondaryConstructor = ShouldGenerateSecondaryConstructor(semanticModel, sourceTypeSymbol);
var mappedProperties = GetSourceMappedProperties(typeSymbol, sourceTypeSymbol, isTypeInheritFromMappedBaseClass); var mappedProperties = GetSourceMappedProperties(typeSymbol, sourceTypeSymbol, isTypeInheritFromMappedBaseClass);
@ -460,6 +510,7 @@ namespace MapTo
sourceTypeIdentifierName, sourceTypeIdentifierName,
sourceTypeSymbol.ToDisplayString(), sourceTypeSymbol.ToDisplayString(),
isTypeUpdatable, isTypeUpdatable,
hasJsonExtension,
mappedProperties, mappedProperties,
allProperties, allProperties,
mappedFields, mappedFields,

View File

@ -31,6 +31,9 @@ namespace MapTo
string SourcePropertyName, string SourcePropertyName,
string? MappedSourcePropertyTypeName, string? MappedSourcePropertyTypeName,
string? EnumerableTypeArgument, string? EnumerableTypeArgument,
ISymbol ActualSymbol,
INamedTypeSymbol? NamedTypeSymbol,
bool isEnumerable,
bool isReadOnly) bool isReadOnly)
{ {
public bool IsEnumerable => EnumerableTypeArgument is not null; public bool IsEnumerable => EnumerableTypeArgument is not null;
@ -46,6 +49,7 @@ namespace MapTo
string SourceTypeIdentifierName, string SourceTypeIdentifierName,
string SourceTypeFullName, string SourceTypeFullName,
bool IsTypeUpdatable, bool IsTypeUpdatable,
bool IsJsonExtension,
ImmutableArray<MappedMember> SourceProperties, ImmutableArray<MappedMember> SourceProperties,
ImmutableArray<MappedMember> TypeProperties, ImmutableArray<MappedMember> TypeProperties,
ImmutableArray<MappedMember> SourceFields, ImmutableArray<MappedMember> SourceFields,

View File

@ -24,7 +24,7 @@ namespace MapTo
.OrderByDescending(s => s.Parameters.Length) .OrderByDescending(s => s.Parameters.Length)
.First(s => s.Name == ".ctor") .First(s => s.Name == ".ctor")
.Parameters .Parameters
.Where(p => !p.HasAttribute(IgnorePropertyAttributeTypeSymbol)) .Where(p => !p.HasAttribute(IgnoreMemberAttributeTypeSymbol))
.Select(property => MapProperty(sourceTypeSymbol, sourceProperties, property)) .Select(property => MapProperty(sourceTypeSymbol, sourceProperties, property))
.Where(mappedProperty => mappedProperty is not null) .Where(mappedProperty => mappedProperty is not null)
.ToImmutableArray()!; .ToImmutableArray()!;
@ -43,7 +43,7 @@ namespace MapTo
.OrderByDescending(s => s.Parameters.Length) .OrderByDescending(s => s.Parameters.Length)
.First(s => s.Name == ".ctor") .First(s => s.Name == ".ctor")
.Parameters .Parameters
.Where(p => !p.HasAttribute(IgnorePropertyAttributeTypeSymbol)) .Where(p => !p.HasAttribute(IgnoreMemberAttributeTypeSymbol))
.Select(property => MapProperty(typeSymbol, sourceProperties, property)) .Select(property => MapProperty(typeSymbol, sourceProperties, property))
.Where(mappedProperty => mappedProperty is not null) .Where(mappedProperty => mappedProperty is not null)
.ToImmutableArray()!; .ToImmutableArray()!;

View File

@ -0,0 +1,60 @@
using static MapTo.Sources.Constants;
namespace MapTo.Sources
{
internal static class DictionaryToListAttributeSource
{
internal const string AttributeName = "DictionaryToList";
internal const string AttributeClassName = AttributeName + "Attribute";
internal const string FullyQualifiedName = RootNamespace + "." + AttributeClassName;
internal const string SourceMemberNameFieldOrPropertyName = "SourcePropertyName";
internal static SourceCode Generate(SourceGenerationOptions options)
{
using var builder = new SourceBuilder()
.WriteLine(GeneratedFilesHeader)
.WriteNullableContextOptionIf(options.SupportNullableReferenceTypes)
.WriteLine()
.WriteLine("using System;")
.WriteLine()
.WriteLine($"namespace {RootNamespace}")
.WriteOpeningBracket();
if (options.GenerateXmlDocument)
{
builder
.WriteLine("/// <summary>")
.WriteLine("/// Specifies the mapping behavior of the annotated property.")
.WriteLine("/// </summary>")
.WriteLine("/// <remarks>")
.WriteLine($"/// {AttributeClassName} has a number of uses:")
.WriteLine("/// <list type=\"bullet\">")
.WriteLine("/// <item><description>By default properties with same name will get mapped. This attribute allows the names to be different.</description></item>")
.WriteLine("/// <item><description>Indicates that a property should be mapped when member serialization is set to opt-in.</description></item>")
.WriteLine("/// </list>")
.WriteLine("/// </remarks>");
}
builder
.WriteLine("[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]")
.WriteLine($"public sealed class {AttributeClassName} : Attribute")
.WriteOpeningBracket();
if (options.GenerateXmlDocument)
{
builder
.WriteLine("/// <summary>")
.WriteLine("/// Gets or sets the property name of the object to mapping from.")
.WriteLine("/// </summary>");
}
builder
.WriteLine($"public string{options.NullableReferenceSyntax} {SourceMemberNameFieldOrPropertyName} {{ get; set; }}")
.WriteClosingBracket() // class
.WriteClosingBracket(); // namespace
return new(builder.ToString(), $"{AttributeClassName}.g.cs");
}
}
}

View File

@ -2,9 +2,9 @@
namespace MapTo.Sources namespace MapTo.Sources
{ {
internal static class IgnorePropertyAttributeSource internal static class IgnoreMemberAttributeSource
{ {
internal const string AttributeName = "IgnoreProperty"; internal const string AttributeName = "IgnoreMemberMapTo";
internal const string AttributeClassName = AttributeName + "Attribute"; internal const string AttributeClassName = AttributeName + "Attribute";
internal const string FullyQualifiedName = RootNamespace + "." + AttributeClassName; internal const string FullyQualifiedName = RootNamespace + "." + AttributeClassName;
@ -26,7 +26,7 @@ namespace MapTo.Sources
} }
builder builder
.WriteLine("[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]") .WriteLine("[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false, AllowMultiple = false)]")
.WriteLine($"public sealed class {AttributeClassName} : Attribute {{ }}") .WriteLine($"public sealed class {AttributeClassName} : Attribute {{ }}")
.WriteClosingBracket(); .WriteClosingBracket();

View File

@ -0,0 +1,40 @@
using static MapTo.Sources.Constants;
namespace MapTo.Sources
{
internal static class JsonExtensionAttributeSource
{
internal const string AttributeName = "JsonExtension";
internal const string AttributeClassName = AttributeName + "Attribute";
internal const string FullyQualifiedName = RootNamespace + "." + AttributeClassName;
internal static SourceCode Generate(SourceGenerationOptions options)
{
using var builder = new SourceBuilder()
.WriteLine(GeneratedFilesHeader)
.WriteLine("using System;")
.WriteLine()
.WriteLine($"namespace {RootNamespace}")
.WriteOpeningBracket();
if (options.GenerateXmlDocument)
{
builder
.WriteLine("/// <summary>")
.WriteLine("/// Specifies that the annotated class has a json extension.")
.WriteLine("/// </summary>");
}
builder
.WriteLine("[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]")
.WriteLine($"public sealed class {AttributeName}Attribute : Attribute")
.WriteOpeningBracket();
builder
.WriteClosingBracket() // class
.WriteClosingBracket(); // namespace
return new(builder.ToString(), $"{AttributeName}Attribute.g.cs");
}
}
}

View File

@ -18,7 +18,7 @@ namespace MapTo
return typeSymbol return typeSymbol
.GetAllMembers() .GetAllMembers()
.OfType<IFieldSymbol>() .OfType<IFieldSymbol>()
.Where(p => !p.HasAttribute(IgnorePropertyAttributeTypeSymbol)) .Where(p => !p.HasAttribute(IgnoreMemberAttributeTypeSymbol))
.Select(property => MapField(sourceTypeSymbol, sourceProperties, property)) .Select(property => MapField(sourceTypeSymbol, sourceProperties, property))
.Where(mappedProperty => mappedProperty is not null) .Where(mappedProperty => mappedProperty is not null)
.ToImmutableArray()!; .ToImmutableArray()!;
@ -31,7 +31,7 @@ namespace MapTo
return typeSymbol return typeSymbol
.GetAllMembers() .GetAllMembers()
.OfType<IPropertySymbol>() .OfType<IPropertySymbol>()
.Where(p => !p.HasAttribute(IgnorePropertyAttributeTypeSymbol)) .Where(p => !p.HasAttribute(IgnoreMemberAttributeTypeSymbol))
.Select(property => MapProperty(sourceTypeSymbol, sourceProperties, property)) .Select(property => MapProperty(sourceTypeSymbol, sourceProperties, property))
.Where(mappedProperty => mappedProperty is not null) .Where(mappedProperty => mappedProperty is not null)
.ToImmutableArray()!; .ToImmutableArray()!;
@ -44,7 +44,7 @@ namespace MapTo
return sourceTypeSymbol return sourceTypeSymbol
.GetAllMembers() .GetAllMembers()
.OfType<IFieldSymbol>() .OfType<IFieldSymbol>()
.Where(p => !p.HasAttribute(IgnorePropertyAttributeTypeSymbol)) .Where(p => !p.HasAttribute(IgnoreMemberAttributeTypeSymbol))
.Select(property => MapFieldSimple(typeSymbol, property)) .Select(property => MapFieldSimple(typeSymbol, property))
.Where(mappedProperty => mappedProperty is not null) .Where(mappedProperty => mappedProperty is not null)
.ToImmutableArray()!; .ToImmutableArray()!;
@ -57,7 +57,7 @@ namespace MapTo
return sourceTypeSymbol return sourceTypeSymbol
.GetAllMembers() .GetAllMembers()
.OfType<IPropertySymbol>() .OfType<IPropertySymbol>()
.Where(p => !p.HasAttribute(IgnorePropertyAttributeTypeSymbol)) .Where(p => !p.HasAttribute(IgnoreMemberAttributeTypeSymbol))
.Select(property => MapPropertySimple(typeSymbol, property)) .Select(property => MapPropertySimple(typeSymbol, property))
.Where(mappedProperty => mappedProperty is not null) .Where(mappedProperty => mappedProperty is not null)
.ToImmutableArray()!; .ToImmutableArray()!;

View File

@ -29,6 +29,7 @@ namespace BlueWest.Data
Sell Sell
} }
[JsonExtension]
[MapFrom(typeof(FinanceTransactionInsertDto))] [MapFrom(typeof(FinanceTransactionInsertDto))]
public partial struct FinanceTransaction public partial struct FinanceTransaction
{ {

View File

@ -5,12 +5,12 @@ namespace BlueWest.Data
public partial struct FinanceTransactionInsertDto public partial struct FinanceTransactionInsertDto
{ {
public int UserId { get; set; } public readonly int UserId;
public FinanceTransactionType FinanceTransactionType { get; } public readonly FinanceTransactionType FinanceTransactionType;
public FinanceSymbol FinanceSymbol { get; } public readonly FinanceSymbol FinanceSymbol;
public double Amount { get; } // To Buy public readonly double Amount; // To Buy
public double Quantity { get; } // Bought public readonly double Quantity; // Bought
public double Fee { get; } public readonly double Fee;
public DateTime DateTime { get; } public readonly DateTime DateTime;
} }
} }

View File

@ -9,14 +9,14 @@ namespace BlueWest.Data
partial struct FinanceTransactionReadDto partial struct FinanceTransactionReadDto
{ {
public int UserId { get; set; } public readonly int UserId;
public FinanceTransactionType FinanceTransactionType { get; } public readonly FinanceTransactionType FinanceTransactionType;
public FinanceSymbol FinanceSymbol { get; } public readonly FinanceSymbol FinanceSymbol;
public double Amount { get; } // To Buy public readonly double Amount; // To Buy
public double Quantity { get; } // Bought public readonly double Quantity; // Bought
public double Fee { get; } public readonly double Fee;
public DateTime DateTime { get; } public readonly DateTime DateTime;
public string ReadData { get; } public readonly string ReadData;
} }
} }

View File

@ -6,6 +6,7 @@ using MapTo;
namespace BlueWest.Data namespace BlueWest.Data
{ {
[MapFrom(typeof(UserUpdateDto))] [MapFrom(typeof(UserUpdateDto))]
[JsonExtension]
public partial class User public partial class User
{ {
public readonly int Id; public readonly int Id;