diff --git a/.github/workflows/publish-packages.yml b/.github/workflows/publish-packages.yml index 1fe26eb..844274e 100644 --- a/.github/workflows/publish-packages.yml +++ b/.github/workflows/publish-packages.yml @@ -26,10 +26,10 @@ jobs: run: dotnet build --configuration Release --no-restore /p:PublicRelease=true - name: Test run: dotnet test --configuration Release --no-build --verbosity normal - - name: Publish MapTo + - name: Publish BlueWest.EfMethods uses: brandedoutcast/publish-nuget@v2.5.5 with: - PROJECT_FILE_PATH: src/BlueWest.MapTo/BlueWest.MapTo.csproj + PROJECT_FILE_PATH: src/BlueWest.EfMethods/BlueWest.EfMethods.csproj NUGET_KEY: ${{secrets.NUGET_API_KEY}} NUGET_SOURCE: https://api.nuget.org TAG_COMMIT: false diff --git a/BlueWest.EfMethods.sln b/BlueWest.EfMethods.sln new file mode 100644 index 0000000..e3b6c06 --- /dev/null +++ b/BlueWest.EfMethods.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlueWest.EfMethods", "src\BlueWest.EfMethods\BlueWest.EfMethods.csproj", "{4DB371AC-48D0-4F01-8EF3-7707D06EF0A7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4DB371AC-48D0-4F01-8EF3-7707D06EF0A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4DB371AC-48D0-4F01-8EF3-7707D06EF0A7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4DB371AC-48D0-4F01-8EF3-7707D06EF0A7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4DB371AC-48D0-4F01-8EF3-7707D06EF0A7}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/MapTo.sln.DotSettings b/BlueWest.EfMethods.sln.DotSettings similarity index 79% rename from MapTo.sln.DotSettings rename to BlueWest.EfMethods.sln.DotSettings index 2397ecf..5e7b2a2 100644 --- a/MapTo.sln.DotSettings +++ b/BlueWest.EfMethods.sln.DotSettings @@ -1,5 +1,5 @@  - True + True True True True diff --git a/MapTo.sln b/MapTo.sln deleted file mode 100644 index a62cb9b..0000000 --- a/MapTo.sln +++ /dev/null @@ -1,34 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapTo", "src\BlueWest.MapTo\BlueWest.MapTo.csproj", "{4DB371AC-48D0-4F01-8EF3-7707D06EF0A7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapToTests", "test\MapTo.Tests\MapTo.Tests.csproj", "{797DA57B-AC7E-468B-8799-44C5A574C0E3}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestConsoleApp", "test\TestConsoleApp\TestConsoleApp.csproj", "{5BE2551A-9EF9-42FA-B6D1-5B5E6A90CC85}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapTo.Integration.Tests", "test\MapTo.Integration.Tests\MapTo.Integration.Tests.csproj", "{23B46FDF-6A1E-4287-88C9-C8C5D7EECB8C}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {4DB371AC-48D0-4F01-8EF3-7707D06EF0A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4DB371AC-48D0-4F01-8EF3-7707D06EF0A7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4DB371AC-48D0-4F01-8EF3-7707D06EF0A7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4DB371AC-48D0-4F01-8EF3-7707D06EF0A7}.Release|Any CPU.Build.0 = Release|Any CPU - {797DA57B-AC7E-468B-8799-44C5A574C0E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {797DA57B-AC7E-468B-8799-44C5A574C0E3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {797DA57B-AC7E-468B-8799-44C5A574C0E3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {797DA57B-AC7E-468B-8799-44C5A574C0E3}.Release|Any CPU.Build.0 = Release|Any CPU - {5BE2551A-9EF9-42FA-B6D1-5B5E6A90CC85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5BE2551A-9EF9-42FA-B6D1-5B5E6A90CC85}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5BE2551A-9EF9-42FA-B6D1-5B5E6A90CC85}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5BE2551A-9EF9-42FA-B6D1-5B5E6A90CC85}.Release|Any CPU.Build.0 = Release|Any CPU - {23B46FDF-6A1E-4287-88C9-C8C5D7EECB8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {23B46FDF-6A1E-4287-88C9-C8C5D7EECB8C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {23B46FDF-6A1E-4287-88C9-C8C5D7EECB8C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {23B46FDF-6A1E-4287-88C9-C8C5D7EECB8C}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal diff --git a/README.md b/README.md index 8db4591..da4ea08 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,22 @@ -# MapTo -[![Nuget](https://img.shields.io/nuget/v/mapto?logo=nuget)](https://www.nuget.org/packages/MapTo/) -![Publish Packages](https://github.com/mrtaikandi/MapTo/workflows/Publish%20Packages/badge.svg) +# BlueWest.EfMethods +[![Nuget](https://img.shields.io/nuget/v/BlueWest.EfMethods?logo=nuget)](https://www.nuget.org/packages/BlueWest.EfMethods/) +![Publish Packages](https://github.com/mrtaikandi/BlueWest.EfMethods/workflows/Publish%20Packages/badge.svg) A convention based object to object mapper using [Roslyn source generator](https://github.com/dotnet/roslyn/blob/master/docs/features/source-generators.md). -MapTo is a library to programmatically generate the necessary code to map one object to another during compile-time, eliminating the need to use reflection to map objects and make it much faster in runtime. It provides compile-time safety checks and ease of use by leveraging extension methods. +BlueWest.EfMethods is a library to programmatically generate the necessary code to map one object to another during compile-time, eliminating the need to use reflection to map objects and make it much faster in runtime. It provides compile-time safety checks and ease of use by leveraging extension methods. ## Installation ``` -dotnet add package MapTo --prerelease +dotnet add package BlueWest.EfMethods --prerelease ``` ## Usage -MapTo relies on a set of attributes to instruct it on how to generate the mappings. To start, declare the destination class as `partial` and annotate it with `MapFrom` attribute. As its name implies, `MapFrom` attribute tells the library what the source class you want to map from is. +BlueWest.EfMethods relies on a set of attributes to instruct it on how to generate the mappings. To start, declare the destination class as `partial` and annotate it with `MapFrom` attribute. As its name implies, `MapFrom` attribute tells the library what the source class you want to map from is. ```c# -using MapTo; +using BlueWest.EfMethods; namespace App.ViewModels { @@ -47,11 +47,11 @@ vm = new UserViewModel(user); // A generated contructor. vm = UserViewModel.From(user); // A generated factory method. ``` -> Please refer to [sample console app](https://github.com/mrtaikandi/MapTo/tree/master/test/TestConsoleApp) for a more complete example. +> Please refer to [sample console app](https://github.com/mrtaikandi/BlueWest.EfMethods/tree/master/test/TestConsoleApp) for a more complete example. ## Available Attributes ### IgnoreProperty -By default, MapTo will include all properties with the same name (case-sensitive), whether read-only or not, in the mapping unless annotating them with the `IgnoreProperty` attribute. +By default, BlueWest.EfMethods will include all properties with the same name (case-sensitive), whether read-only or not, in the mapping unless annotating them with the `IgnoreProperty` attribute. ```c# [IgnoreProperty] public string FullName { get; set; } diff --git a/src/BlueWest.MapTo/BlueWest.MapTo.csproj b/src/BlueWest.EfMethods/BlueWest.EfMethods.csproj similarity index 78% rename from src/BlueWest.MapTo/BlueWest.MapTo.csproj rename to src/BlueWest.EfMethods/BlueWest.EfMethods.csproj index b6400ce..6b0251b 100644 --- a/src/BlueWest.MapTo/BlueWest.MapTo.csproj +++ b/src/BlueWest.EfMethods/BlueWest.EfMethods.csproj @@ -7,17 +7,17 @@ An object to object mapping generator using Roslyn source generator. true true - MapTo - https://github.com/mrtaikandi/mapto + BlueWest.EfMethods + https://github.com/mrtaikandi/BlueWest.EfMethods $(Version) true - https://github.com/mrtaikandi/mapto + https://github.com/mrtaikandi/BlueWest.EfMethods snupkg - MapTo + BlueWest.EfMethods - bin\Release\MapTo.xml + bin\Release\BlueWest.EfMethods.xml false @@ -40,7 +40,7 @@ - + diff --git a/src/BlueWest.EfMethods/BlueWest.EfMethods.props b/src/BlueWest.EfMethods/BlueWest.EfMethods.props new file mode 100644 index 0000000..3ea8d66 --- /dev/null +++ b/src/BlueWest.EfMethods/BlueWest.EfMethods.props @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/BlueWest.MapTo/CompilerServices/IsExternalInit.cs b/src/BlueWest.EfMethods/CompilerServices/IsExternalInit.cs similarity index 100% rename from src/BlueWest.MapTo/CompilerServices/IsExternalInit.cs rename to src/BlueWest.EfMethods/CompilerServices/IsExternalInit.cs diff --git a/src/BlueWest.MapTo/CompilerServices/NullableAttributes.cs b/src/BlueWest.EfMethods/CompilerServices/NullableAttributes.cs similarity index 100% rename from src/BlueWest.MapTo/CompilerServices/NullableAttributes.cs rename to src/BlueWest.EfMethods/CompilerServices/NullableAttributes.cs diff --git a/src/BlueWest.MapTo/DiagnosticsFactory.cs b/src/BlueWest.EfMethods/DiagnosticsFactory.cs similarity index 56% rename from src/BlueWest.MapTo/DiagnosticsFactory.cs rename to src/BlueWest.EfMethods/DiagnosticsFactory.cs index 0cfe0a8..2b9e05a 100644 --- a/src/BlueWest.MapTo/DiagnosticsFactory.cs +++ b/src/BlueWest.EfMethods/DiagnosticsFactory.cs @@ -1,11 +1,11 @@ using System.Linq; -using MapTo.Extensions; -using MapTo.Sources; +using BlueWest.EfMethods.Extensions; +using BlueWest.EfMethods.Sources; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; -using static MapTo.Sources.Constants; +using static BlueWest.EfMethods.Sources.Constants; -namespace MapTo +namespace BlueWest.EfMethods { internal static class DiagnosticsFactory { @@ -17,19 +17,10 @@ namespace MapTo internal static Diagnostic TypeNotFoundError(Location location, string syntaxName) => Create($"{ErrorId}010", location, $"Unable to find '{syntaxName}' type."); - - internal static Diagnostic MapFromAttributeNotFoundError(Location location) => - Create($"{ErrorId}020", location, $"Unable to find {MapFromAttributeSource.AttributeName} type."); - + internal static Diagnostic NoMatchingPropertyFoundError(Location location, INamedTypeSymbol classType, INamedTypeSymbol sourceType) => Create($"{ErrorId}030", location, $"No matching properties found between '{classType.ToDisplayString()}' and '{sourceType.ToDisplayString()}' types."); - 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 '{IgnoreMemberAttributeSource.FullyQualifiedName}'."); - - 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()}>'."); - internal static Diagnostic ConfigurationParseError(string error) => Create($"{ErrorId}040", Location.None, error); diff --git a/src/BlueWest.MapTo/EfMethods/AttributeSources/EfGeneratorAttributeSource.cs b/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfGeneratorAttributeSource.cs similarity index 94% rename from src/BlueWest.MapTo/EfMethods/AttributeSources/EfGeneratorAttributeSource.cs rename to src/BlueWest.EfMethods/EfMethods/AttributeSources/EfGeneratorAttributeSource.cs index a742cf3..867c9d9 100644 --- a/src/BlueWest.MapTo/EfMethods/AttributeSources/EfGeneratorAttributeSource.cs +++ b/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfGeneratorAttributeSource.cs @@ -1,6 +1,6 @@ -using static MapTo.Sources.Constants; +using static BlueWest.EfMethods.Sources.Constants; -namespace MapTo.Sources +namespace BlueWest.EfMethods.Sources { public class EfGeneratorAttributeSource { diff --git a/src/BlueWest.MapTo/EfMethods/AttributeSources/EfGetManyAttributeSource.cs b/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfGetManyAttributeSource.cs similarity index 95% rename from src/BlueWest.MapTo/EfMethods/AttributeSources/EfGetManyAttributeSource.cs rename to src/BlueWest.EfMethods/EfMethods/AttributeSources/EfGetManyAttributeSource.cs index 5e854ec..3a7869b 100644 --- a/src/BlueWest.MapTo/EfMethods/AttributeSources/EfGetManyAttributeSource.cs +++ b/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfGetManyAttributeSource.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Text; -using static MapTo.Sources.Constants; +using static BlueWest.EfMethods.Sources.Constants; -namespace MapTo.Sources +namespace BlueWest.EfMethods.Sources { internal static class EfGetManyAttributeSource { diff --git a/src/BlueWest.MapTo/EfMethods/AttributeSources/EfGetOneByAttributeSource.cs b/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfGetOneByAttributeSource.cs similarity index 95% rename from src/BlueWest.MapTo/EfMethods/AttributeSources/EfGetOneByAttributeSource.cs rename to src/BlueWest.EfMethods/EfMethods/AttributeSources/EfGetOneByAttributeSource.cs index aa14c77..00e5c35 100644 --- a/src/BlueWest.MapTo/EfMethods/AttributeSources/EfGetOneByAttributeSource.cs +++ b/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfGetOneByAttributeSource.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Text; -using static MapTo.Sources.Constants; +using static BlueWest.EfMethods.Sources.Constants; -namespace MapTo.Sources +namespace BlueWest.EfMethods.Sources { internal class EfGetOneByAttributeSource { diff --git a/src/BlueWest.MapTo/EfMethods/AttributeSources/EfUpdateMethodsAttributeSource.cs b/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfUpdateMethodsAttributeSource.cs similarity index 94% rename from src/BlueWest.MapTo/EfMethods/AttributeSources/EfUpdateMethodsAttributeSource.cs rename to src/BlueWest.EfMethods/EfMethods/AttributeSources/EfUpdateMethodsAttributeSource.cs index f112fa5..acbaeda 100644 --- a/src/BlueWest.MapTo/EfMethods/AttributeSources/EfUpdateMethodsAttributeSource.cs +++ b/src/BlueWest.EfMethods/EfMethods/AttributeSources/EfUpdateMethodsAttributeSource.cs @@ -1,6 +1,6 @@ -using static MapTo.Sources.Constants; +using static BlueWest.EfMethods.Sources.Constants; -namespace MapTo.Sources +namespace BlueWest.EfMethods.Sources { public class EfUpdateMethodsAttributeSource { diff --git a/src/BlueWest.MapTo/EfMethods/AttributeSources/GetOneWIthAttributeSource.cs b/src/BlueWest.EfMethods/EfMethods/AttributeSources/GetOneWIthAttributeSource.cs similarity index 95% rename from src/BlueWest.MapTo/EfMethods/AttributeSources/GetOneWIthAttributeSource.cs rename to src/BlueWest.EfMethods/EfMethods/AttributeSources/GetOneWIthAttributeSource.cs index 6482796..a6d3698 100644 --- a/src/BlueWest.MapTo/EfMethods/AttributeSources/GetOneWIthAttributeSource.cs +++ b/src/BlueWest.EfMethods/EfMethods/AttributeSources/GetOneWIthAttributeSource.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Text; -using static MapTo.Sources.Constants; +using static BlueWest.EfMethods.Sources.Constants; -namespace MapTo.Sources +namespace BlueWest.EfMethods.Sources { internal static class EfGetOneAttributeSource { diff --git a/src/BlueWest.MapTo/EfMethods/EfAddMethodsAttributeSource.cs b/src/BlueWest.EfMethods/EfMethods/EfAddMethodsAttributeSource.cs similarity index 94% rename from src/BlueWest.MapTo/EfMethods/EfAddMethodsAttributeSource.cs rename to src/BlueWest.EfMethods/EfMethods/EfAddMethodsAttributeSource.cs index 552213d..05aff2d 100644 --- a/src/BlueWest.MapTo/EfMethods/EfAddMethodsAttributeSource.cs +++ b/src/BlueWest.EfMethods/EfMethods/EfAddMethodsAttributeSource.cs @@ -1,7 +1,7 @@ -using static MapTo.Sources.Constants; +using static BlueWest.EfMethods.Sources.Constants; -namespace MapTo.Sources +namespace BlueWest.EfMethods.Sources { internal static class EfAddMethodsAttributeSource { diff --git a/src/BlueWest.MapTo/EfMethods/EfGeneratorContext.cs b/src/BlueWest.EfMethods/EfMethods/EfGeneratorContext.cs similarity index 98% rename from src/BlueWest.MapTo/EfMethods/EfGeneratorContext.cs rename to src/BlueWest.EfMethods/EfMethods/EfGeneratorContext.cs index c9aa572..b44f9ed 100644 --- a/src/BlueWest.MapTo/EfMethods/EfGeneratorContext.cs +++ b/src/BlueWest.EfMethods/EfMethods/EfGeneratorContext.cs @@ -4,8 +4,8 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Threading; -using MapTo.Extensions; -using MapTo.Sources; +using BlueWest.EfMethods.Extensions; +using BlueWest.EfMethods.Sources; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -13,7 +13,7 @@ using CSharpExtensions = Microsoft.CodeAnalysis.CSharpExtensions; #pragma warning disable CS8602 -namespace MapTo +namespace BlueWest.EfMethods { internal class EfGeneratorContext { @@ -213,7 +213,8 @@ namespace MapTo { if (!property.TryGetTypeSymbol(out var propertyType)) { - AddDiagnostic(DiagnosticsFactory.NoMatchingPropertyTypeFoundError(property)); + // Todo add diagnostic + //AddDiagnostic(DiagnosticsFactory.NoMatchingPropertyTypeFoundError(property)); namedTypeSymbolResult = null; return false; } diff --git a/src/BlueWest.MapTo/EfMethods/EfMethodsGenerator.cs b/src/BlueWest.EfMethods/EfMethods/EfMethodsGenerator.cs similarity index 97% rename from src/BlueWest.MapTo/EfMethods/EfMethodsGenerator.cs rename to src/BlueWest.EfMethods/EfMethods/EfMethodsGenerator.cs index 8685271..73f48e9 100644 --- a/src/BlueWest.MapTo/EfMethods/EfMethodsGenerator.cs +++ b/src/BlueWest.EfMethods/EfMethods/EfMethodsGenerator.cs @@ -3,12 +3,12 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; -using MapTo.Extensions; -using MapTo.Sources; +using BlueWest.EfMethods.Extensions; +using BlueWest.EfMethods.Sources; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace MapTo +namespace BlueWest.EfMethods { /// /// Ef methods source generator diff --git a/src/BlueWest.MapTo/EfMethods/EfMethodsSource.cs b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource.cs similarity index 96% rename from src/BlueWest.MapTo/EfMethods/EfMethodsSource.cs rename to src/BlueWest.EfMethods/EfMethods/EfMethodsSource.cs index d4886f2..9f9a2e4 100644 --- a/src/BlueWest.MapTo/EfMethods/EfMethodsSource.cs +++ b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource.cs @@ -3,11 +3,11 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; -using MapTo.Extensions; +using BlueWest.EfMethods.Extensions; using Microsoft.CodeAnalysis.CSharp; -using static MapTo.Sources.Constants; +using static BlueWest.EfMethods.Sources.Constants; -namespace MapTo.Sources +namespace BlueWest.EfMethods.Sources { internal static class EfMethodsSource { diff --git a/src/BlueWest.MapTo/EfMethods/EfMethodsSource/EfAddEntityTemplateSource.cs b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfAddEntityTemplateSource.cs similarity index 97% rename from src/BlueWest.MapTo/EfMethods/EfMethodsSource/EfAddEntityTemplateSource.cs rename to src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfAddEntityTemplateSource.cs index a75441a..ce22977 100644 --- a/src/BlueWest.MapTo/EfMethods/EfMethodsSource/EfAddEntityTemplateSource.cs +++ b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfAddEntityTemplateSource.cs @@ -1,9 +1,9 @@ -using MapTo.Extensions; +using BlueWest.EfMethods.Extensions; using System; using System.Collections.Generic; using System.Text; -namespace MapTo.Sources +namespace BlueWest.EfMethods.Sources { internal static class EfAddEntityTemplateSource { diff --git a/src/BlueWest.MapTo/EfMethods/EfMethodsSource/EfGetOneEntityByTemplate.cs b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetOneEntityByTemplate.cs similarity index 96% rename from src/BlueWest.MapTo/EfMethods/EfMethodsSource/EfGetOneEntityByTemplate.cs rename to src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetOneEntityByTemplate.cs index 5ed311f..794be4a 100644 --- a/src/BlueWest.MapTo/EfMethods/EfMethodsSource/EfGetOneEntityByTemplate.cs +++ b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetOneEntityByTemplate.cs @@ -1,10 +1,10 @@ -using MapTo.Extensions; -using MapTo.Sources; +using BlueWest.EfMethods.Extensions; +using BlueWest.EfMethods.Sources; using System; using System.Collections.Generic; using System.Text; -namespace MapTo +namespace BlueWest.EfMethods { internal static class EfGetOneEntityByTemplate { diff --git a/src/BlueWest.MapTo/EfMethods/EfMethodsSource/EfGetOneWithTemplateSource.cs b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetOneWithTemplateSource.cs similarity index 97% rename from src/BlueWest.MapTo/EfMethods/EfMethodsSource/EfGetOneWithTemplateSource.cs rename to src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetOneWithTemplateSource.cs index 29a1c00..b1551cb 100644 --- a/src/BlueWest.MapTo/EfMethods/EfMethodsSource/EfGetOneWithTemplateSource.cs +++ b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfGetOneWithTemplateSource.cs @@ -1,10 +1,10 @@ -using MapTo.Extensions; -using MapTo.Sources; +using BlueWest.EfMethods.Extensions; +using BlueWest.EfMethods.Sources; using System; using System.Collections.Generic; using System.Text; -namespace MapTo +namespace BlueWest.EfMethods { internal static class EfGetOneWithTemplateSource { diff --git a/src/BlueWest.MapTo/EfMethods/EfMethodsSource/EfUpdateEntityTemplateSource.cs b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfUpdateEntityTemplateSource.cs similarity index 97% rename from src/BlueWest.MapTo/EfMethods/EfMethodsSource/EfUpdateEntityTemplateSource.cs rename to src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfUpdateEntityTemplateSource.cs index c87eae6..634cb35 100644 --- a/src/BlueWest.MapTo/EfMethods/EfMethodsSource/EfUpdateEntityTemplateSource.cs +++ b/src/BlueWest.EfMethods/EfMethods/EfMethodsSource/EfUpdateEntityTemplateSource.cs @@ -1,9 +1,9 @@ -using MapTo.Extensions; +using BlueWest.EfMethods.Extensions; using System; using System.Collections.Generic; using System.Text; -namespace MapTo.Sources +namespace BlueWest.EfMethods.Sources { internal static class EfUpdateEntityTemplateSource { diff --git a/src/BlueWest.MapTo/EfMethodsSyntaxReceiver.cs b/src/BlueWest.EfMethods/EfMethodsSyntaxReceiver.cs similarity index 96% rename from src/BlueWest.MapTo/EfMethodsSyntaxReceiver.cs rename to src/BlueWest.EfMethods/EfMethodsSyntaxReceiver.cs index b3f253c..fa39183 100644 --- a/src/BlueWest.MapTo/EfMethodsSyntaxReceiver.cs +++ b/src/BlueWest.EfMethods/EfMethodsSyntaxReceiver.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; using System.Linq; -using MapTo.Sources; +using BlueWest.EfMethods.Sources; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace MapTo +namespace BlueWest.EfMethods { internal enum EfMethodsAttributeType { diff --git a/src/BlueWest.EfMethods/Extensions/CommonExtensions.cs b/src/BlueWest.EfMethods/Extensions/CommonExtensions.cs new file mode 100644 index 0000000..01adc79 --- /dev/null +++ b/src/BlueWest.EfMethods/Extensions/CommonExtensions.cs @@ -0,0 +1,31 @@ +using BlueWest.EfMethods.Sources; +using Microsoft.CodeAnalysis; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BlueWest.EfMethods.Extensions +{ + internal static class CommonExtensions + { + internal static SourceBuilder WriteComment(this SourceBuilder builder, string comment = "") + { + return builder.WriteLine($"// {comment}"); + } + + internal static SourceBuilder WriteCommentArray(this SourceBuilder builder, IEnumerable 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; + } + } +} diff --git a/src/BlueWest.MapTo/Extensions/EnumExtensions.cs b/src/BlueWest.EfMethods/Extensions/EnumExtensions.cs similarity index 80% rename from src/BlueWest.MapTo/Extensions/EnumExtensions.cs rename to src/BlueWest.EfMethods/Extensions/EnumExtensions.cs index f170a05..3d60c07 100644 --- a/src/BlueWest.MapTo/Extensions/EnumExtensions.cs +++ b/src/BlueWest.EfMethods/Extensions/EnumExtensions.cs @@ -1,6 +1,6 @@ using System; -namespace MapTo.Extensions +namespace BlueWest.EfMethods.Extensions { internal static class EnumExtensions { diff --git a/src/BlueWest.MapTo/Extensions/EnumerableExtensions.cs b/src/BlueWest.EfMethods/Extensions/EnumerableExtensions.cs similarity index 91% rename from src/BlueWest.MapTo/Extensions/EnumerableExtensions.cs rename to src/BlueWest.EfMethods/Extensions/EnumerableExtensions.cs index 7823e58..e84ede9 100644 --- a/src/BlueWest.MapTo/Extensions/EnumerableExtensions.cs +++ b/src/BlueWest.EfMethods/Extensions/EnumerableExtensions.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; -namespace MapTo.Extensions +namespace BlueWest.EfMethods.Extensions { internal static class EnumerableExtensions { diff --git a/src/BlueWest.MapTo/Extensions/GeneratorExecutionContextExtensions.cs b/src/BlueWest.EfMethods/Extensions/GeneratorExecutionContextExtensions.cs similarity index 94% rename from src/BlueWest.MapTo/Extensions/GeneratorExecutionContextExtensions.cs rename to src/BlueWest.EfMethods/Extensions/GeneratorExecutionContextExtensions.cs index 4b85fc8..54ed80e 100644 --- a/src/BlueWest.MapTo/Extensions/GeneratorExecutionContextExtensions.cs +++ b/src/BlueWest.EfMethods/Extensions/GeneratorExecutionContextExtensions.cs @@ -4,11 +4,11 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Text; -namespace MapTo.Extensions +namespace BlueWest.EfMethods.Extensions { internal static class GeneratorExecutionContextExtensions { - private const string PropertyNameSuffix = "MapTo_"; + private const string PropertyNameSuffix = "BlueWest.EfMethods_"; internal static T GetBuildGlobalOption(this GeneratorExecutionContext context, string propertyName, T defaultValue = default!) where T : notnull { diff --git a/src/BlueWest.MapTo/Extensions/RoslynExtensions.cs b/src/BlueWest.EfMethods/Extensions/RoslynExtensions.cs similarity index 99% rename from src/BlueWest.MapTo/Extensions/RoslynExtensions.cs rename to src/BlueWest.EfMethods/Extensions/RoslynExtensions.cs index aff2cff..273cb89 100644 --- a/src/BlueWest.MapTo/Extensions/RoslynExtensions.cs +++ b/src/BlueWest.EfMethods/Extensions/RoslynExtensions.cs @@ -5,7 +5,7 @@ using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace MapTo.Extensions +namespace BlueWest.EfMethods.Extensions { internal static class RoslynExtensions { diff --git a/src/BlueWest.MapTo/Extensions/StringBuilderExtensions.cs b/src/BlueWest.EfMethods/Extensions/StringBuilderExtensions.cs similarity index 95% rename from src/BlueWest.MapTo/Extensions/StringBuilderExtensions.cs rename to src/BlueWest.EfMethods/Extensions/StringBuilderExtensions.cs index 5d26819..f5f3456 100644 --- a/src/BlueWest.MapTo/Extensions/StringBuilderExtensions.cs +++ b/src/BlueWest.EfMethods/Extensions/StringBuilderExtensions.cs @@ -1,7 +1,7 @@ using System; using System.Text; -namespace MapTo.Extensions +namespace BlueWest.EfMethods.Extensions { internal static class StringBuilderExtensions { diff --git a/src/BlueWest.MapTo/Extensions/StringExtensions.cs b/src/BlueWest.EfMethods/Extensions/StringExtensions.cs similarity index 92% rename from src/BlueWest.MapTo/Extensions/StringExtensions.cs rename to src/BlueWest.EfMethods/Extensions/StringExtensions.cs index 8e1437a..e912990 100644 --- a/src/BlueWest.MapTo/Extensions/StringExtensions.cs +++ b/src/BlueWest.EfMethods/Extensions/StringExtensions.cs @@ -1,6 +1,6 @@ using System.Threading.Tasks; -namespace MapTo.Extensions +namespace BlueWest.EfMethods.Extensions { internal static class StringExtensions { diff --git a/src/BlueWest.MapTo/Models.cs b/src/BlueWest.EfMethods/Models.cs similarity index 82% rename from src/BlueWest.MapTo/Models.cs rename to src/BlueWest.EfMethods/Models.cs index 6e08087..fbc772e 100644 --- a/src/BlueWest.MapTo/Models.cs +++ b/src/BlueWest.EfMethods/Models.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Immutable; -using MapTo.Extensions; +using BlueWest.EfMethods.Extensions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -namespace MapTo +namespace BlueWest.EfMethods { internal enum AccessModifier { @@ -22,56 +22,6 @@ namespace MapTo internal record SourceCode(string Text, string HintName); - internal record MappedMember( - string Name, - string FullyQualifiedType, - string Type, - string? TypeConverter, - ImmutableArray TypeConverterParameters, - string SourcePropertyName, - string? MappedSourcePropertyTypeName, - string? EnumerableTypeArgument, - ISymbol ActualSymbol, - INamedTypeSymbol? NamedTypeSymbol, - bool isEnumerable, - bool isReadOnly) - { - public bool IsEnumerable => EnumerableTypeArgument is not null; - } - - internal record MappedSourceType - ( - string SourceNamespace, - string SourceTypeIdentifierName, - string SourceTypeFullName, - ImmutableArray SourceProperties, - ImmutableArray SourceFields, - ImmutableArray TypeProperties, - ImmutableArray TypeFields, - bool GenerateSecondaryConstructor - ) - { - public string SourceType => SourceTypeFullName; - } - - - internal record MappingModel( - SourceGenerationOptions Options, - string? Namespace, - SyntaxTokenList Modifiers, - string Type, - string TypeIdentifierName, - bool IsTypeUpdatable, - bool IsJsonExtension, - ImmutableArray MappedSourceTypes, - bool HasMappedBaseClass, - ImmutableArray Usings - ); - - internal interface IEfMethodsModel { } - - - internal record EfMethodsModel( SourceGenerationOptions Options, diff --git a/src/BlueWest.MapTo/Sources/Constants.cs b/src/BlueWest.EfMethods/Sources/Constants.cs similarity index 52% rename from src/BlueWest.MapTo/Sources/Constants.cs rename to src/BlueWest.EfMethods/Sources/Constants.cs index f25d2ce..fdbef40 100644 --- a/src/BlueWest.MapTo/Sources/Constants.cs +++ b/src/BlueWest.EfMethods/Sources/Constants.cs @@ -1,8 +1,8 @@ -namespace MapTo.Sources +namespace BlueWest.EfMethods.Sources { internal class Constants { - internal const string RootNamespace = "MapTo"; + internal const string RootNamespace = "BlueWest.EfMethods"; internal const string GeneratedFilesHeader = "// "; } } \ No newline at end of file diff --git a/src/BlueWest.MapTo/Sources/SourceBuilder.cs b/src/BlueWest.EfMethods/Sources/SourceBuilder.cs similarity index 98% rename from src/BlueWest.MapTo/Sources/SourceBuilder.cs rename to src/BlueWest.EfMethods/Sources/SourceBuilder.cs index 369fe9e..978356e 100644 --- a/src/BlueWest.MapTo/Sources/SourceBuilder.cs +++ b/src/BlueWest.EfMethods/Sources/SourceBuilder.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; -namespace MapTo.Sources +namespace BlueWest.EfMethods.Sources { internal sealed class SourceBuilder : IDisposable { diff --git a/src/BlueWest.MapTo/ClassMappingContext.cs b/src/BlueWest.MapTo/ClassMappingContext.cs deleted file mode 100644 index 14b023d..0000000 --- a/src/BlueWest.MapTo/ClassMappingContext.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Collections.Immutable; -using System.Linq; -using MapTo.Extensions; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace MapTo -{ - internal class ClassMappingContext : MappingContext - { - internal ClassMappingContext(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, TypeDeclarationSyntax typeSyntax) - : base(compilation, sourceGenerationOptions, typeSyntax) { } - - protected override ImmutableArray GetSourceMappedFields(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass) - { - var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType().ToArray(); - - return typeSymbol - .GetAllMembers() - .OfType() - .Where(p => !p.HasAttribute(IgnoreMemberAttributeTypeSymbol)) - .Select(property => MapField(sourceTypeSymbol, sourceProperties, property)) - .Where(mappedProperty => mappedProperty is not null) - .ToImmutableArray()!; - } - - protected override ImmutableArray GetSourceMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool hasInheritedClass) - { - var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType().ToArray(); - - return typeSymbol - .GetAllMembers() - .OfType() - .Where(p => !p.HasAttribute(IgnoreMemberAttributeTypeSymbol)) - .Select(property => MapProperty(sourceTypeSymbol, sourceProperties, property)) - .Where(mappedProperty => mappedProperty is not null) - .ToImmutableArray()!; - } - - protected override ImmutableArray GetTypeMappedFields(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass) - { - var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType().ToArray(); - - return sourceTypeSymbol - .GetAllMembers() - .OfType() - .Where(p => !p.HasAttribute(IgnoreMemberAttributeTypeSymbol)) - .Select(property => MapFieldSimple(typeSymbol, property)) - .Where(mappedProperty => mappedProperty is not null) - .ToImmutableArray()!; - } - - protected override ImmutableArray GetTypeMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool hasInheritedClass) - { - var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType().ToArray(); - - return sourceTypeSymbol - .GetAllMembers() - .OfType() - .Where(p => !p.HasAttribute(IgnoreMemberAttributeTypeSymbol)) - .Select(property => MapPropertySimple(typeSymbol, property)) - .Where(mappedProperty => mappedProperty is not null) - .ToImmutableArray()!; - } - - } -} \ No newline at end of file diff --git a/src/BlueWest.MapTo/Extensions/CommonExtensions.cs b/src/BlueWest.MapTo/Extensions/CommonExtensions.cs deleted file mode 100644 index 459486f..0000000 --- a/src/BlueWest.MapTo/Extensions/CommonExtensions.cs +++ /dev/null @@ -1,86 +0,0 @@ -using MapTo.Sources; -using Microsoft.CodeAnalysis; -using System; -using System.Collections.Generic; -using System.Text; - -namespace MapTo.Extensions -{ - internal static class CommonExtensions - { - internal static SourceBuilder WriteComment(this SourceBuilder builder, string comment = "") - { - return builder.WriteLine($"// {comment}"); - } - - internal static SourceBuilder WriteCommentArray(this SourceBuilder builder, IEnumerable 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) - { - foreach (var targetSourceType in model.MappedSourceTypes) - { - builder - .WriteLine() - .WriteComment($" IsTypeUpdatable {model.IsTypeUpdatable}") - .WriteComment($" HasMappedBaseClass {model.HasMappedBaseClass.ToString()}") - .WriteComment($" Namespace {model.Namespace}") - .WriteComment($" Options {model.Options.ToString()}") - .WriteComment($" Type {model.Type}") - .WriteComment($" TypeIdentifierName {model.TypeIdentifierName}") - .WriteComment($" SourceNamespace {targetSourceType.SourceNamespace}") - .WriteComment($" SourceTypeFullName {targetSourceType.SourceTypeFullName}") - .WriteComment($" SourceTypeIdentifierName {targetSourceType.SourceTypeIdentifierName}"); - } - - return builder; - - } - - internal static SourceBuilder WriteMappedProperties(this SourceBuilder builder, System.Collections.Immutable.ImmutableArray 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}") - .WriteComment($" Type {item.Type}") - .WriteComment($" MappedSourcePropertyTypeName {item.MappedSourcePropertyTypeName}") - .WriteComment($" IsEnumerable {item.IsEnumerable}") - .WriteComment($" FullyQualifiedType {item.FullyQualifiedType}") - .WriteComment($" EnumerableTypeArgument {item.EnumerableTypeArgument}") - .WriteComment($" SourcePropertyName {item.SourcePropertyName}") - .WriteComment($" TypeSymbol {item.FullyQualifiedType.ToString()}") - .WriteComment($" isReadOnly {item.isReadOnly.ToString()}") - .WriteComment($" isEnumerable {item.isEnumerable.ToString()}") - .WriteComment($" INamedTypeSymbol {item.NamedTypeSymbol?.ToString()}") - .WriteComment($" INamedTypeSymbolTypeArguments {str}") - - .WriteLine(); - } - - return builder; - } - - } -} diff --git a/src/BlueWest.MapTo/Extensions/CommonSource.cs b/src/BlueWest.MapTo/Extensions/CommonSource.cs deleted file mode 100644 index ffec021..0000000 --- a/src/BlueWest.MapTo/Extensions/CommonSource.cs +++ /dev/null @@ -1,379 +0,0 @@ -using MapTo.Sources; -using static MapTo.Sources.Constants; -using System; -using System.Collections.Generic; -using System.Text; -using System.Collections.Immutable; -using System.Diagnostics; -using Microsoft.CodeAnalysis; -#pragma warning disable CS8602 - -namespace MapTo.Extensions -{ - internal static class CommonSource - { - internal static SourceCode GenerateStructOrClass(this MappingModel model, string structOrClass) - { - const bool writeDebugInfo = false; - - List constructorHeaders = new List(); - - using var builder = new SourceBuilder() - .WriteLine(GeneratedFilesHeader) - .WriteNullableContextOptionIf(model.Options.SupportNullableReferenceTypes) - .WriteUsings(model.Usings) - .WriteLine() - // Namespace declaration - .WriteLine($"namespace {model.Namespace}") - .WriteOpeningBracket(); - - builder - // Class declaration - .WriteLine($"partial {structOrClass} {model.TypeIdentifierName}") - .WriteOpeningBracket() - .WriteLine() - // Class body - .GeneratePublicEmptyConstructor(model, ref constructorHeaders, true) - .GeneratePublicConstructor(model, ref constructorHeaders) - .GeneratePublicConstructor(model, ref constructorHeaders, true); - - - if (model.IsTypeUpdatable) builder.GenerateUpdateMethod(model); - if (model.IsJsonExtension) builder.WriteToJsonMethod(model); - - - builder - .WriteLine() - // End class declaration - .WriteClosingBracket() - .WriteLine() - // End namespace declaration - .WriteClosingBracket(); - - return new(builder.ToString(), $"{model.Namespace}.{model.TypeIdentifierName}.g.cs"); - } - - private static SourceBuilder GeneratePublicConstructor(this SourceBuilder builder, MappingModel model, ref List constructorHeaders, bool filterNonMapped = false) - { - const string mappingContextParameterName = "context"; - - foreach (var targetSourceType in model.MappedSourceTypes) - { - var sourceClassParameterName = targetSourceType.SourceTypeIdentifierName.ToCamelCase(); - var baseConstructor = /*model.HasMappedBaseClass ? $" : base({mappingContextParameterName}, {sourceClassParameterName})" :*/ string.Empty; - var stringBuilder = new StringBuilder(); - var otherProperties = new List(); - - foreach (var property in targetSourceType.TypeProperties) - { - if (!targetSourceType.SourceProperties.IsMappedProperty(property) && !filterNonMapped) - { - stringBuilder.Append(", "); - stringBuilder.Append($"{property.FullyQualifiedType} {property.SourcePropertyName.ToCamelCase()}"); - otherProperties.Add(property); - } - - } - foreach (var property in targetSourceType.TypeFields) - { - if (!targetSourceType.SourceFields.IsMappedProperty(property) && !filterNonMapped) - { - stringBuilder.Append(", "); - stringBuilder.Append($"{property.FullyQualifiedType} {property.SourcePropertyName.ToCamelCase()}"); - otherProperties.Add(property); - } - } - - var readOnlyPropertiesArguments = stringBuilder.ToString(); - - var constructorHeader = - $"public {model.TypeIdentifierName}({targetSourceType.SourceType} {sourceClassParameterName}{readOnlyPropertiesArguments}){baseConstructor}"; - - - bool hasAlreadyConstructor = false; - - foreach (var header in constructorHeaders) - { - if(constructorHeader.Contains(header)) hasAlreadyConstructor = true; - } - - if (hasAlreadyConstructor) continue; - - constructorHeaders.Add(constructorHeader); - - builder - .WriteLine(constructorHeader) - .WriteOpeningBracket() - .WriteAssignmentMethod(model, filterNonMapped ? null : otherProperties.ToArray().ToImmutableArray(), sourceClassParameterName, mappingContextParameterName, filterNonMapped); - - builder.WriteClosingBracket() - .WriteLine(); - } - - // End constructor declaration - return builder; - } - - private static SourceBuilder GeneratePublicEmptyConstructor(this SourceBuilder builder, MappingModel model, ref List constructorHeaders, bool filterNonMapped = false) - { - const string mappingContextParameterName = "context"; - - foreach (var targetSourceType in model.MappedSourceTypes) - { - var baseConstructor = /*model.HasMappedBaseClass ? $" : base({mappingContextParameterName}, {sourceClassParameterName})" :*/ string.Empty; - - var constructorHeader = - $"public {model.TypeIdentifierName}(){baseConstructor}"; - - - bool hasAlreadyConstructor = false; - - foreach (var header in constructorHeaders) - { - if(constructorHeader.Contains(header)) hasAlreadyConstructor = true; - } - - if (hasAlreadyConstructor) continue; - - constructorHeaders.Add(constructorHeader); - - builder - .WriteLine(constructorHeader) - .WriteOpeningBracket() - .WriteClosingBracket() - .WriteLine(); - } - - // End constructor declaration - return builder; - } - - private static bool IsMappedProperty(this System.Collections.Immutable.ImmutableArray properties, MappedMember property) - { - - foreach (var prop in properties) - { - if (prop.Name == property.Name) return true; - } - - 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 targetSourceType in model.MappedSourceTypes) - { - foreach (var property in targetSourceType.TypeProperties) - { - if (!property.isEnumerable) - HandlePropertyEnumerable(builder, property); - else - { - builder = WriteJsonField(builder, property); - } - } - foreach (var property in targetSourceType.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; -#pragma warning disable CS8602 - builder.WriteCommentArray(symbol.Parameters, nameof(symbol.Parameters)); -#pragma warning restore CS8602 - 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? otherProperties, - string? sourceClassParameterName, string mappingContextParameterName, bool fromUpdate) - { - - List _addedMembers = new List(); - - foreach (var targetSourceType in model.MappedSourceTypes) - { - foreach (var property in targetSourceType.SourceProperties) - { - if (property.isReadOnly && fromUpdate) continue; - if(_addedMembers.Contains(property)) continue; - - - builder.WriteLine($"{property.Name} = {sourceClassParameterName}.{property.SourcePropertyName};"); - _addedMembers.Add(property); - - } - - foreach (var property in targetSourceType.SourceFields) - { - if (property.isReadOnly && fromUpdate) continue; - if(_addedMembers.Contains(property)) continue; - - builder.WriteLine($"{property.Name} = {sourceClassParameterName}.{property.SourcePropertyName};"); - _addedMembers.Add(property); - - } - - if (otherProperties == null) return builder; - - foreach (var property in otherProperties) - { - if(_addedMembers.Contains(property)) continue; - - builder.WriteLine(property.MappedSourcePropertyTypeName is null - ? $"{property.Name} = {property.SourcePropertyName.ToCamelCase()};" - : ""); - _addedMembers.Add(property); - - - } - } - - - - return builder; - - } - - - private static SourceBuilder GenerateUpdateMethod(this SourceBuilder builder, MappingModel model) - { - - foreach (var targetSourceType in model.MappedSourceTypes) - { - var sourceClassParameterName = targetSourceType.SourceTypeIdentifierName.ToCamelCase(); - - builder - .GenerateUpdaterMethodsXmlDocs(model, sourceClassParameterName, targetSourceType) - .WriteLine($"public void Update({targetSourceType.SourceType} {sourceClassParameterName})") - .WriteOpeningBracket() - .WriteAssignmentMethod(model, null, sourceClassParameterName, "context", true) - .WriteClosingBracket() - .WriteLine(); - } - - - return builder; - } - - private static SourceBuilder GenerateUpdaterMethodsXmlDocs(this SourceBuilder builder, MappingModel model, string sourceClassParameterName, - MappedSourceType mappedSourceType) - { - if (!model.Options.GenerateXmlDocument) - { - return builder; - } - builder - .WriteLine("/// ") - .WriteLine($"/// Updates and sets its participating properties") - .WriteLine($"/// using the property values from .") - .WriteLine("/// ") - .WriteLine($"/// The instance of to use as source."); - return builder; - } - - private static SourceBuilder GenerateEnumerableJsonSourceTypeExtensionMethod(this SourceBuilder builder, MappingModel model) - { - - foreach (var targetSourceType in model.MappedSourceTypes) - { - var sourceClassParameterName = targetSourceType.SourceTypeIdentifierName.ToCamelCase(); - - builder - .WriteLineIf(model.Options.SupportNullableStaticAnalysis, $"[return: NotNullIfNotNull(\"{sourceClassParameterName}\")]") - .WriteLine($"{model.Options.GeneratedMethodsAccessModifier.ToLowercaseString()} static string ToJson(this IEnumerable<{targetSourceType.SourceType}{model.Options.NullableReferenceSyntax}> {sourceClassParameterName}List)") - .WriteOpeningBracket() - .WriteLine($"return {sourceClassParameterName} == null ? null : new {model.TypeIdentifierName}({sourceClassParameterName});") - .WriteClosingBracket(); - } - - - return builder; - } - } -} diff --git a/src/BlueWest.MapTo/MapTo.props b/src/BlueWest.MapTo/MapTo.props deleted file mode 100644 index 2082ff5..0000000 --- a/src/BlueWest.MapTo/MapTo.props +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/BlueWest.MapTo/MapToGenerator.cs b/src/BlueWest.MapTo/MapToGenerator.cs deleted file mode 100644 index 57c5a31..0000000 --- a/src/BlueWest.MapTo/MapToGenerator.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using MapTo.Extensions; -using MapTo.Sources; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace MapTo -{ - /// - /// MapTo source generator. - /// - [Generator] - public class MapToGenerator : ISourceGenerator - { - /// - public void Initialize(GeneratorInitializationContext context) - { - - context.RegisterForSyntaxNotifications(() => new MapToSyntaxReceiver()); - - } - - /// - public void Execute(GeneratorExecutionContext context) - { - try - { - var options = SourceGenerationOptions.From(context); - - var compilation = context.Compilation - .AddSource(ref context, UseUpdateAttributeSource.Generate(options)) - .AddSource(ref context, JsonExtensionAttributeSource.Generate(options)) - .AddSource(ref context, MapFromAttributeSource.Generate(options)) - .AddSource(ref context, IgnoreMemberAttributeSource.Generate(options)) - .AddSource(ref context, ITypeConverterSource.Generate(options)) - .AddSource(ref context, MapTypeConverterAttributeSource.Generate(options)) - .AddSource(ref context, MapPropertyAttributeSource.Generate(options)) - .AddSource(ref context, MappingContextSource.Generate(options)); - - if (context.SyntaxReceiver is MapToSyntaxReceiver receiver && receiver.CandidateTypes.Any()) - { - AddGeneratedMappingsClasses(context, compilation, receiver.CandidateTypes, options); - } - } - catch (Exception ex) - { - Console.WriteLine(ex); - throw; - } - } - - private static void AddGeneratedMappingsClasses(GeneratorExecutionContext context, Compilation compilation, IEnumerable candidateTypes, SourceGenerationOptions options) - { - foreach (var typeDeclarationSyntax in candidateTypes) - { - var mappingContext = MappingContext.Create(compilation, options, typeDeclarationSyntax); - - mappingContext.Diagnostics.ForEach(context.ReportDiagnostic); - - if (mappingContext.Model is null) - { - continue; - } - - var (source, hintName) = typeDeclarationSyntax switch - { - StructDeclarationSyntax => MapStructSource.Generate(mappingContext.Model), - ClassDeclarationSyntax => MapClassSource.Generate(mappingContext.Model), - RecordDeclarationSyntax => MapRecordSource.Generate(mappingContext.Model), - _ => throw new ArgumentOutOfRangeException() - }; - - context.AddSource(hintName, source); - } - } - } -} \ No newline at end of file diff --git a/src/BlueWest.MapTo/MapToSyntaxReceiver.cs b/src/BlueWest.MapTo/MapToSyntaxReceiver.cs deleted file mode 100644 index b0a83c0..0000000 --- a/src/BlueWest.MapTo/MapToSyntaxReceiver.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using MapTo.Sources; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace MapTo -{ - internal class MapToSyntaxReceiver : ISyntaxReceiver - { - public List CandidateTypes { get; } = new(); - - /// - public void OnVisitSyntaxNode(SyntaxNode syntaxNode) - { - if (syntaxNode is not TypeDeclarationSyntax { AttributeLists: { Count: >= 1 } attributes } typeDeclarationSyntax) - { - return; - } - - var attributeSyntax = attributes - .SelectMany(a => a.Attributes) - .FirstOrDefault(a => a.Name is - IdentifierNameSyntax { Identifier: { ValueText: MapFromAttributeSource.AttributeName } } // For: [MapFrom] - or - QualifiedNameSyntax // For: [MapTo.MapFrom] - { - Left: IdentifierNameSyntax { Identifier: { ValueText: Constants.RootNamespace } }, - Right: IdentifierNameSyntax { Identifier: { ValueText: MapFromAttributeSource.AttributeName } } - } - ); - - if (attributeSyntax is not null) - { - CandidateTypes.Add(typeDeclarationSyntax); - } - } - } -} \ No newline at end of file diff --git a/src/BlueWest.MapTo/MappingContext.cs b/src/BlueWest.MapTo/MappingContext.cs deleted file mode 100644 index e9665ba..0000000 --- a/src/BlueWest.MapTo/MappingContext.cs +++ /dev/null @@ -1,630 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using MapTo.Extensions; -using MapTo.Sources; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; -#pragma warning disable CS8602 - -namespace MapTo -{ - internal static class MappingContextExtensions - { - internal static ImmutableArray GetReadOnlyMappedProperties(this ImmutableArray mappedProperties) => mappedProperties.Where(p => p.isReadOnly).ToImmutableArray()!; - internal static ImmutableArray GetWritableMappedProperties(this ImmutableArray mappedProperties) => mappedProperties.Where(p => !p.isReadOnly).ToImmutableArray()!; - } - - internal abstract class MappingContext - { - private readonly List _ignoredNamespaces; - - protected MappingContext(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, TypeDeclarationSyntax typeSyntax) - { - _ignoredNamespaces = new(); - Diagnostics = ImmutableArray.Empty; - Usings = ImmutableArray.Create("System", Constants.RootNamespace); - SourceGenerationOptions = sourceGenerationOptions; - TypeSyntax = typeSyntax; - Compilation = compilation; - - IgnoreMemberAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(IgnoreMemberAttributeSource.FullyQualifiedName); - MapTypeConverterAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(MapTypeConverterAttributeSource.FullyQualifiedName); - TypeConverterInterfaceTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(ITypeConverterSource.FullyQualifiedName); - MapPropertyAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(MapPropertyAttributeSource.FullyQualifiedName); - MapFromAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(MapFromAttributeSource.FullyQualifiedName); - UseUpdateAttributeTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(UseUpdateAttributeSource.FullyQualifiedName); - JsonExtensionAttributeSymbol = compilation.GetTypeByMetadataNameOrThrow(JsonExtensionAttributeSource.FullyQualifiedName); - MappingContextTypeSymbol = compilation.GetTypeByMetadataNameOrThrow(MappingContextSource.FullyQualifiedName); - - AddUsingIfRequired(sourceGenerationOptions.SupportNullableStaticAnalysis, "System.Diagnostics.CodeAnalysis"); - } - - public ImmutableArray Diagnostics { get; private set; } - - public MappingModel? Model { get; private set; } - - protected Compilation Compilation { get; } - - protected INamedTypeSymbol IgnoreMemberAttributeTypeSymbol { get; } - - protected INamedTypeSymbol MapFromAttributeTypeSymbol { get; } - - protected INamedTypeSymbol UseUpdateAttributeTypeSymbol { get; } - - public static INamedTypeSymbol JsonExtensionAttributeSymbol { get; set; } = null!; - - protected INamedTypeSymbol MappingContextTypeSymbol { get; } - - protected INamedTypeSymbol MapPropertyAttributeTypeSymbol { get; } - - protected INamedTypeSymbol MapTypeConverterAttributeTypeSymbol { get; } - - protected SourceGenerationOptions SourceGenerationOptions { get; } - - protected INamedTypeSymbol TypeConverterInterfaceTypeSymbol { get; } - - protected TypeDeclarationSyntax TypeSyntax { get; } - - protected ImmutableArray Usings { get; private set; } - - public static MappingContext Create(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, TypeDeclarationSyntax typeSyntax) - { - - MappingContext context = typeSyntax switch - { - StructDeclarationSyntax => new StructMappingContext(compilation, sourceGenerationOptions, typeSyntax), - ClassDeclarationSyntax => new ClassMappingContext(compilation, sourceGenerationOptions, typeSyntax), - RecordDeclarationSyntax => new RecordMappingContext(compilation, sourceGenerationOptions, typeSyntax), - _ => throw new ArgumentOutOfRangeException() - }; - - context.Model = context.CreateMappingModel(); - - return context; - } - - protected void AddDiagnostic(Diagnostic diagnostic) - { - Diagnostics = Diagnostics.Add(diagnostic); - } - - protected void AddUsingIfRequired(ISymbol? namedTypeSymbol) => - AddUsingIfRequired(namedTypeSymbol?.ContainingNamespace.IsGlobalNamespace == false, namedTypeSymbol?.ContainingNamespace); - - protected void AddUsingIfRequired(bool condition, INamespaceSymbol? ns) => - AddUsingIfRequired(condition && ns is not null && !_ignoredNamespaces.Contains(ns.ToDisplayParts().First()), ns?.ToDisplayString()); - - protected void AddUsingIfRequired(bool condition, string? ns) - { - if (ns is not null && condition && ns != TypeSyntax.GetNamespace() && !Usings.Contains(ns)) - { - Usings = Usings.Add(ns); - } - } - - protected IPropertySymbol? FindSourceProperty(IEnumerable sourceProperties, ISymbol property) - { - var propertyName = property - .GetAttribute(MapPropertyAttributeTypeSymbol) - ?.NamedArguments - .FirstOrDefault(a => a.Key == MapPropertyAttributeSource.SourcePropertyNamePropertyName) - .Value.Value as string ?? property.Name; - - return sourceProperties.FirstOrDefault(p => p.Name == propertyName); - } - protected IFieldSymbol? FindSourceField(IEnumerable sourceProperties, ISymbol property) - { - var propertyName = property - .GetAttribute(MapPropertyAttributeTypeSymbol) - ?.NamedArguments - .FirstOrDefault(a => a.Key == MapPropertyAttributeSource.SourcePropertyNamePropertyName) - .Value.Value as string ?? property.Name; - - return sourceProperties.FirstOrDefault(p => p.Name == propertyName); - } - - protected abstract ImmutableArray GetSourceMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass); - protected abstract ImmutableArray GetTypeMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass); - - - protected abstract ImmutableArray GetSourceMappedFields(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass); - protected abstract ImmutableArray GetTypeMappedFields(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass); - - - protected ImmutableArray GetSourceTypeSymbol(TypeDeclarationSyntax typeDeclarationSyntax, SemanticModel? semanticModel = null) - { - var attributeData = typeDeclarationSyntax.GetAttribute(MapFromAttributeSource.AttributeName); - var sourceSymbol = GetSourceTypeSymbol(attributeData, semanticModel); - return sourceSymbol; - } - - // we need two possible InamedTypeSymbol - protected ImmutableArray GetSourceTypeSymbol(SyntaxNode? attributeSyntax, SemanticModel? semanticModel = null) - { - if (attributeSyntax is null) - { - return new ImmutableArray(){}; - } - - semanticModel ??= Compilation.GetSemanticModel(attributeSyntax.SyntaxTree); - var descendentNodes = attributeSyntax - .DescendantNodes(); - - var sourceTypeExpressionSyntax = descendentNodes - .OfType() - .ToImmutableArray(); - - // cast - var resultList = new List(); - for (int i = 0; i < sourceTypeExpressionSyntax.Length; i++) - { - var sourceTypeExpression = sourceTypeExpressionSyntax[i]; - if (semanticModel.GetTypeInfo(sourceTypeExpression.Type).Type is INamedTypeSymbol namedTypeSymbol) - { - resultList.Add(namedTypeSymbol); - } - } - - return resultList.ToImmutableArray(); - } - - protected bool IsTypeInheritFromMappedBaseClass(SemanticModel semanticModel) - { - return TypeSyntax.BaseList is not null && TypeSyntax.BaseList.Types - .Select(t => semanticModel.GetTypeInfo(t.Type).Type) - .Any(t => t?.GetAttribute(MapFromAttributeTypeSymbol) != null); - } - - protected bool IsTypeUpdatable() - { - return TypeSyntax.GetAttribute("UseUpdate") != null; - } - protected bool HasJsonExtension() - { - return TypeSyntax.GetAttribute("JsonExtension") != null; - } - protected virtual MappedMember? MapProperty(ISymbol sourceTypeSymbol, IReadOnlyCollection sourceProperties, ISymbol property) - { - var sourceProperty = FindSourceProperty(sourceProperties, property); - if (sourceProperty is null || !property.TryGetTypeSymbol(out var propertyType)) - { - return null; - } - - - string? converterFullyQualifiedName = null; - var converterParameters = ImmutableArray.Empty; - ITypeSymbol? mappedSourcePropertyType = null; - ITypeSymbol? enumerableTypeArgumentType = null; - - if (!Compilation.HasCompatibleTypes(sourceProperty, property)) - { - if (!TryGetMapTypeConverterForProperty(property, sourceProperty, out converterFullyQualifiedName, out converterParameters) && - !TryGetNestedObjectMappings(property, out mappedSourcePropertyType, out enumerableTypeArgumentType)) - { - return null; - } - } - - AddUsingIfRequired(propertyType); - AddUsingIfRequired(enumerableTypeArgumentType); - AddUsingIfRequired(mappedSourcePropertyType); - - INamedTypeSymbol? namedType; - var isEnumerable = IsEnumerable(property, out namedType); - - - return new MappedMember( - property.Name, - property.GetTypeSymbol().ToString(), - ToQualifiedDisplayName(propertyType) ?? propertyType.Name, - converterFullyQualifiedName, - converterParameters.ToImmutableArray(), - sourceProperty.Name, - ToQualifiedDisplayName(mappedSourcePropertyType), - ToQualifiedDisplayName(enumerableTypeArgumentType), - property, - namedType, - isEnumerable, - (property as IPropertySymbol).IsReadOnly); -; - } - - protected virtual MappedMember? MapField(ISymbol sourceTypeSymbol, IReadOnlyCollection sourceProperties, ISymbol property) - { - var sourceProperty = FindSourceField(sourceProperties, property); - if (sourceProperty is null || !property.TryGetTypeSymbol(out var propertyType)) - { - return null; - } - - if (property is IFieldSymbol symbol) - { - if (symbol.AssociatedSymbol != null) return null; - } - - string? converterFullyQualifiedName = null; - var converterParameters = ImmutableArray.Empty; - ITypeSymbol? mappedSourcePropertyType = null; - ITypeSymbol? enumerableTypeArgumentType = null; - - if (!Compilation.HasCompatibleTypes(sourceProperty, property)) - { - if (!TryGetMapTypeConverterForField(property, sourceProperty, out converterFullyQualifiedName, out converterParameters) && - !TryGetNestedObjectMappings(property, out mappedSourcePropertyType, out enumerableTypeArgumentType)) - { - return null; - } - } - - AddUsingIfRequired(propertyType); - AddUsingIfRequired(enumerableTypeArgumentType); - AddUsingIfRequired(mappedSourcePropertyType); - - - INamedTypeSymbol? namedType; - var isEnumerable = IsEnumerable(property, out namedType); - - return new MappedMember( - property.Name, - property.GetTypeSymbol()?.ToString() ?? throw new InvalidOperationException(), - ToQualifiedDisplayName(propertyType) ?? propertyType.Name, - converterFullyQualifiedName, - converterParameters.ToImmutableArray(), - sourceProperty.Name, - ToQualifiedDisplayName(mappedSourcePropertyType), - ToQualifiedDisplayName(enumerableTypeArgumentType), - property, - namedType, - isEnumerable, - (property as IFieldSymbol).IsReadOnly); - ; - } - protected virtual MappedMember? MapPropertySimple(ISymbol sourceTypeSymbol, ISymbol property) - { - if (!property.TryGetTypeSymbol(out var propertyType)) - { - return null; - } - - - string? converterFullyQualifiedName = null; - var converterParameters = ImmutableArray.Empty; - ITypeSymbol? mappedSourcePropertyType = null; - ITypeSymbol? enumerableTypeArgumentType = null; - - - AddUsingIfRequired(propertyType); - AddUsingIfRequired(enumerableTypeArgumentType); - AddUsingIfRequired(mappedSourcePropertyType); - - INamedTypeSymbol? namedType; - var isEnumerable = IsEnumerable(property, out namedType); - - return new MappedMember( - property.Name, - property.GetTypeSymbol().ToString(), - ToQualifiedDisplayName(propertyType) ?? propertyType.Name, - converterFullyQualifiedName, - converterParameters.ToImmutableArray(), - property.Name, - ToQualifiedDisplayName(mappedSourcePropertyType), - ToQualifiedDisplayName(enumerableTypeArgumentType), - property, - namedType, - isEnumerable, -#pragma warning disable CS8602 - (property as IPropertySymbol).IsReadOnly); -#pragma warning restore CS8602 - ; - } - - protected virtual MappedMember? MapFieldSimple(ISymbol sourceTypeSymbol, ISymbol property) - { - if (!property.TryGetTypeSymbol(out var propertyType)) - { - return null; - } - - if(property is IFieldSymbol symbol) - { - if (symbol.AssociatedSymbol != null) return null; - } - - - string? converterFullyQualifiedName = null; - var converterParameters = ImmutableArray.Empty; - ITypeSymbol? mappedSourcePropertyType = null; - ITypeSymbol? enumerableTypeArgumentType = null; - - - AddUsingIfRequired(propertyType); - AddUsingIfRequired(enumerableTypeArgumentType); - AddUsingIfRequired(mappedSourcePropertyType); - - INamedTypeSymbol? namedType; - var isEnumerable = IsEnumerable(property, out namedType); - - - return new MappedMember( - property.Name, - property.GetTypeSymbol().ToString(), - ToQualifiedDisplayName(propertyType) ?? propertyType.Name, - converterFullyQualifiedName, - converterParameters.ToImmutableArray(), - property.Name, - ToQualifiedDisplayName(mappedSourcePropertyType), - ToQualifiedDisplayName(enumerableTypeArgumentType), - property, - namedType, - isEnumerable, - (property as IFieldSymbol).IsReadOnly); - ; - } - protected bool TryGetMapTypeConverterForProperty(ISymbol property, IPropertySymbol sourceProperty, out string? converterFullyQualifiedName, - out ImmutableArray converterParameters) - { - converterFullyQualifiedName = null; - converterParameters = ImmutableArray.Empty; - - if (!Diagnostics.IsEmpty()) - { - return false; - } - - var typeConverterAttribute = property.GetAttribute(MapTypeConverterAttributeTypeSymbol); - if (typeConverterAttribute?.ConstructorArguments.First().Value is not INamedTypeSymbol converterTypeSymbol) - { - return false; - } - - var baseInterface = GetTypeConverterBaseInterfaceForProperty(converterTypeSymbol, property, sourceProperty); - if (baseInterface is null) - { - AddDiagnostic(DiagnosticsFactory.InvalidTypeConverterGenericTypesError(property, sourceProperty)); - return false; - } - - converterFullyQualifiedName = converterTypeSymbol.ToDisplayString(); - converterParameters = GetTypeConverterParameters(typeConverterAttribute); - return true; - } - protected bool TryGetMapTypeConverterForField(ISymbol property, IFieldSymbol sourceProperty, out string? converterFullyQualifiedName, - out ImmutableArray converterParameters) - { - converterFullyQualifiedName = null; - converterParameters = ImmutableArray.Empty; - - if (!Diagnostics.IsEmpty()) - { - return false; - } - - var typeConverterAttribute = property.GetAttribute(MapTypeConverterAttributeTypeSymbol); - if (typeConverterAttribute?.ConstructorArguments.First().Value is not INamedTypeSymbol converterTypeSymbol) - { - return false; - } - - var baseInterface = GetTypeConverterBaseInterfaceForField(converterTypeSymbol, property, sourceProperty); - if (baseInterface is null) - { - //AddDiagnostic(DiagnosticsFactory.InvalidTypeConverterGenericTypesError(property, null)); - return false; - } - - converterFullyQualifiedName = converterTypeSymbol.ToDisplayString(); - converterParameters = GetTypeConverterParameters(typeConverterAttribute); - return true; - } - protected bool TryGetNestedObjectMappings(ISymbol property, out ITypeSymbol? mappedSourcePropertyType, out ITypeSymbol? enumerableTypeArgument) - { - mappedSourcePropertyType = null; - enumerableTypeArgument = null; - - if (!Diagnostics.IsEmpty()) - { - return false; - } - - if (!property.TryGetTypeSymbol(out var propertyType)) - { - AddDiagnostic(DiagnosticsFactory.NoMatchingPropertyTypeFoundError(property)); - return false; - } - - var mapFromAttribute = propertyType.GetAttribute(MapFromAttributeTypeSymbol); - if (mapFromAttribute is null && - propertyType is INamedTypeSymbol namedTypeSymbol && - !propertyType.IsPrimitiveType() && - (Compilation.IsGenericEnumerable(propertyType) || propertyType.AllInterfaces.Any(i => Compilation.IsGenericEnumerable(i)))) - { - enumerableTypeArgument = namedTypeSymbol.TypeArguments.First(); - mapFromAttribute = enumerableTypeArgument.GetAttribute(MapFromAttributeTypeSymbol); - } - - mappedSourcePropertyType = mapFromAttribute?.ConstructorArguments.First().Value as INamedTypeSymbol; - - if (mappedSourcePropertyType is null && enumerableTypeArgument is null) - { - AddDiagnostic(DiagnosticsFactory.NoMatchingPropertyTypeFoundError(property)); - } - - 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 GetTypeConverterParameters(AttributeData typeConverterAttribute) - { - var converterParameter = typeConverterAttribute.ConstructorArguments.Skip(1).FirstOrDefault(); - return converterParameter.IsNull - ? ImmutableArray.Empty - : converterParameter.Values.Where(v => v.Value is not null).Select(v => v.Value!.ToSourceCodeString()).ToImmutableArray(); - } - - private MappingModel? CreateMappingModel() - { - var semanticModel = Compilation.GetSemanticModel(TypeSyntax.SyntaxTree); - if (semanticModel.GetDeclaredSymbol(TypeSyntax) is not INamedTypeSymbol typeSymbol) - { - AddDiagnostic(DiagnosticsFactory.TypeNotFoundError(TypeSyntax.GetLocation(), TypeSyntax.Identifier.ValueText)); - return null; - } - - // We can have 2 sources... - - var sourceTypeSymbols = GetSourceTypeSymbol(TypeSyntax, semanticModel); - - - // lets pick one for now, and then think what to do with the second one - if (sourceTypeSymbols.IsDefaultOrEmpty) - { - AddDiagnostic(DiagnosticsFactory.MapFromAttributeNotFoundError(TypeSyntax.GetLocation())); - return null; - } - - - var typeIdentifierName = TypeSyntax.GetIdentifierName(); - var isTypeInheritFromMappedBaseClass = IsTypeInheritFromMappedBaseClass(semanticModel); - var isTypeUpdatable = true; //IsTypeUpdatable(); - var hasJsonExtension = false; // HasJsonExtension(); - - List mappedSourceTypes = new List(); - - foreach (var sourceTypeSymbol in sourceTypeSymbols) - { - _ignoredNamespaces.Add(sourceTypeSymbol.ContainingNamespace.ToDisplayParts().First()); - var sourceTypeIdentifierName = sourceTypeSymbol.Name; - var shouldGenerateSecondaryConstructor = ShouldGenerateSecondaryConstructor(semanticModel, sourceTypeSymbol); - var mappedProperties = GetSourceMappedProperties(typeSymbol, sourceTypeSymbol, isTypeInheritFromMappedBaseClass); - var mappedFields = GetSourceMappedFields(typeSymbol, sourceTypeSymbol, isTypeInheritFromMappedBaseClass); - AddUsingIfRequired(mappedProperties.Any(p => p.IsEnumerable), "System.Linq"); - var allProperties = GetTypeMappedProperties(sourceTypeSymbol, typeSymbol , isTypeInheritFromMappedBaseClass); - var allFields = GetTypeMappedFields(sourceTypeSymbol, typeSymbol, isTypeInheritFromMappedBaseClass); - - mappedSourceTypes.Add(new MappedSourceType( - sourceTypeSymbol.ContainingNamespace.ToDisplayString(), - sourceTypeIdentifierName, - sourceTypeSymbol.ToDisplayString(), - mappedProperties, mappedFields, allProperties, allFields, shouldGenerateSecondaryConstructor)); - } - - //var sourceTypeSymbol = sourceTypeSymbols[0]; - - // Pick first one to avoid errors. TODO: Make possible to use different source types - - - - - /*if (!mappedProperties.Any()) - { - AddDiagnostic(DiagnosticsFactory.NoMatchingPropertyFoundError(TypeSyntax.GetLocation(), typeSymbol, sourceTypeSymbol)); - return null; - }*/ - - return new MappingModel( - SourceGenerationOptions, - TypeSyntax.GetNamespace(), - TypeSyntax.Modifiers, - TypeSyntax.Keyword.Text, - typeIdentifierName, - isTypeUpdatable, - hasJsonExtension, - mappedSourceTypes.ToImmutableArray(), - isTypeInheritFromMappedBaseClass, - Usings); - } - - - - private INamedTypeSymbol? GetTypeConverterBaseInterfaceForProperty(ITypeSymbol converterTypeSymbol, ISymbol property, IPropertySymbol sourceProperty) - { - if (!property.TryGetTypeSymbol(out var propertyType)) - { - return null; - } - - return converterTypeSymbol.AllInterfaces - .FirstOrDefault(i => - i.TypeArguments.Length == 2 && - SymbolEqualityComparer.Default.Equals(i.ConstructedFrom, TypeConverterInterfaceTypeSymbol) && - SymbolEqualityComparer.Default.Equals(sourceProperty.Type, i.TypeArguments[0]) && - SymbolEqualityComparer.Default.Equals(propertyType, i.TypeArguments[1])); - } - private INamedTypeSymbol? GetTypeConverterBaseInterfaceForField(ITypeSymbol converterTypeSymbol, ISymbol property, IFieldSymbol sourceProperty) - { - if (!property.TryGetTypeSymbol(out var propertyType)) - { - return null; - } - - return converterTypeSymbol.AllInterfaces - .FirstOrDefault(i => - i.TypeArguments.Length == 2 && - SymbolEqualityComparer.Default.Equals(i.ConstructedFrom, TypeConverterInterfaceTypeSymbol) && - SymbolEqualityComparer.Default.Equals(sourceProperty.Type, i.TypeArguments[0]) && - SymbolEqualityComparer.Default.Equals(propertyType, i.TypeArguments[1])); - } - - private bool ShouldGenerateSecondaryConstructor(SemanticModel semanticModel, ISymbol sourceTypeSymbol) - { - var constructorSyntax = TypeSyntax.DescendantNodes() - .OfType() - .FirstOrDefault(c => - c.ParameterList.Parameters.Count == 1 && - SymbolEqualityComparer.Default.Equals(semanticModel.GetTypeInfo(c.ParameterList.Parameters.Single().Type!).ConvertedType, sourceTypeSymbol)); - - if (constructorSyntax is null) - { - // Secondary constructor is not defined. - return true; - } - - if (constructorSyntax.Initializer?.ArgumentList.Arguments is not { Count: 2 } arguments || - !SymbolEqualityComparer.Default.Equals(semanticModel.GetTypeInfo(arguments[0].Expression).ConvertedType, MappingContextTypeSymbol) || - !SymbolEqualityComparer.Default.Equals(semanticModel.GetTypeInfo(arguments[1].Expression).ConvertedType, sourceTypeSymbol)) - { - AddDiagnostic(DiagnosticsFactory.MissingConstructorArgument(constructorSyntax)); - } - - return false; - } - - private string? ToQualifiedDisplayName(ISymbol? symbol) - { - if (symbol is null) - { - return null; - } - - var containingNamespace = TypeSyntax.GetNamespace(); - var symbolNamespace = symbol.ContainingNamespace.ToDisplayString(); - return containingNamespace != symbolNamespace && _ignoredNamespaces.Contains(symbol.ContainingNamespace.ToDisplayParts().First()) - ? symbol.ToDisplayString() - : symbol.Name; - } - } -} \ No newline at end of file diff --git a/src/BlueWest.MapTo/RecordMappingContext.cs b/src/BlueWest.MapTo/RecordMappingContext.cs deleted file mode 100644 index 657ccd9..0000000 --- a/src/BlueWest.MapTo/RecordMappingContext.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Collections.Immutable; -using System.Linq; -using MapTo.Extensions; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace MapTo -{ - internal class RecordMappingContext : MappingContext - { - internal RecordMappingContext(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, TypeDeclarationSyntax typeSyntax) - : base(compilation, sourceGenerationOptions, typeSyntax) { } - - protected override ImmutableArray GetSourceMappedFields(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass) - { - throw new System.NotImplementedException(); - } - - protected override ImmutableArray GetSourceMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass) - { - var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType().ToArray(); - return typeSymbol.GetMembers() - .OfType() - .OrderByDescending(s => s.Parameters.Length) - .First(s => s.Name == ".ctor") - .Parameters - .Where(p => !p.HasAttribute(IgnoreMemberAttributeTypeSymbol)) - .Select(property => MapProperty(sourceTypeSymbol, sourceProperties, property)) - .Where(mappedProperty => mappedProperty is not null) - .ToImmutableArray()!; - } - - protected override ImmutableArray GetTypeMappedFields(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass) - { - throw new System.NotImplementedException(); - } - - protected override ImmutableArray GetTypeMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass) - { - var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType().ToArray(); - return typeSymbol.GetMembers() - .OfType() - .OrderByDescending(s => s.Parameters.Length) - .First(s => s.Name == ".ctor") - .Parameters - .Where(p => !p.HasAttribute(IgnoreMemberAttributeTypeSymbol)) - .Select(property => MapProperty(typeSymbol, sourceProperties, property)) - .Where(mappedProperty => mappedProperty is not null) - .ToImmutableArray()!; - } - } -} \ No newline at end of file diff --git a/src/BlueWest.MapTo/Sources/DictionaryToListAttributeSource.cs b/src/BlueWest.MapTo/Sources/DictionaryToListAttributeSource.cs deleted file mode 100644 index 19622c4..0000000 --- a/src/BlueWest.MapTo/Sources/DictionaryToListAttributeSource.cs +++ /dev/null @@ -1,60 +0,0 @@ -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("/// ") - .WriteLine("/// Specifies the mapping behavior of the annotated property.") - .WriteLine("/// ") - .WriteLine("/// ") - .WriteLine($"/// {AttributeClassName} has a number of uses:") - .WriteLine("/// ") - .WriteLine("/// By default properties with same name will get mapped. This attribute allows the names to be different.") - .WriteLine("/// Indicates that a property should be mapped when member serialization is set to opt-in.") - .WriteLine("/// ") - .WriteLine("/// "); - } - - builder - .WriteLine("[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]") - .WriteLine($"public sealed class {AttributeClassName} : Attribute") - .WriteOpeningBracket(); - - if (options.GenerateXmlDocument) - { - builder - .WriteLine("/// ") - .WriteLine("/// Gets or sets the property name of the object to mapping from.") - .WriteLine("/// "); - } - - builder - .WriteLine($"public string{options.NullableReferenceSyntax} {SourceMemberNameFieldOrPropertyName} {{ get; set; }}") - .WriteClosingBracket() // class - .WriteClosingBracket(); // namespace - - - return new(builder.ToString(), $"{AttributeClassName}.g.cs"); - } - } -} \ No newline at end of file diff --git a/src/BlueWest.MapTo/Sources/ITypeConverterSource.cs b/src/BlueWest.MapTo/Sources/ITypeConverterSource.cs deleted file mode 100644 index 3b627b8..0000000 --- a/src/BlueWest.MapTo/Sources/ITypeConverterSource.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Microsoft.CodeAnalysis; -using static MapTo.Sources.Constants; - -namespace MapTo.Sources -{ - [SuppressMessage("ReSharper", "InconsistentNaming")] - internal static class ITypeConverterSource - { - internal const string InterfaceName = "ITypeConverter"; - internal const string FullyQualifiedName = RootNamespace + "." + InterfaceName + "`2"; - - internal static SourceCode Generate(SourceGenerationOptions options) - { - using var builder = new SourceBuilder() - .WriteLine(GeneratedFilesHeader) - .WriteNullableContextOptionIf(options.SupportNullableReferenceTypes) - .WriteLine() - .WriteLine($"namespace {RootNamespace}") - .WriteOpeningBracket(); - - if (options.GenerateXmlDocument) - { - builder - .WriteLine("/// ") - .WriteLine("/// Converts the value of to .") - .WriteLine("/// ") - .WriteLine("/// The type to convert from.") - .WriteLine("/// The type to convert to."); - } - - builder - .WriteLine($"public interface {InterfaceName}") - .WriteOpeningBracket(); - - if (options.GenerateXmlDocument) - { - builder - .WriteLine("/// ") - .WriteLine("/// Converts the value of object to .") - .WriteLine("/// ") - .WriteLine("/// The to convert.") - .WriteLine($"/// The parameter list passed to the ") - .WriteLine("/// object."); - } - - builder - .WriteLine($"TDestination Convert(TSource source, object[]{options.NullableReferenceSyntax} converterParameters);") - .WriteClosingBracket() - .WriteClosingBracket(); - - return new(builder.ToString(), $"{InterfaceName}.g.cs"); - } - - internal static string GetFullyQualifiedName(ITypeSymbol sourceType, ITypeSymbol destinationType) => - $"{RootNamespace}.{InterfaceName}<{sourceType.ToDisplayString()}, {destinationType.ToDisplayString()}>"; - } -} \ No newline at end of file diff --git a/src/BlueWest.MapTo/Sources/IgnoreMemberAttributeSource.cs b/src/BlueWest.MapTo/Sources/IgnoreMemberAttributeSource.cs deleted file mode 100644 index 4f889b8..0000000 --- a/src/BlueWest.MapTo/Sources/IgnoreMemberAttributeSource.cs +++ /dev/null @@ -1,36 +0,0 @@ -using static MapTo.Sources.Constants; - -namespace MapTo.Sources -{ - internal static class IgnoreMemberAttributeSource - { - internal const string AttributeName = "IgnoreMemberMapTo"; - internal const string AttributeClassName = AttributeName + "Attribute"; - internal const string FullyQualifiedName = RootNamespace + "." + AttributeClassName; - - internal static SourceCode Generate(SourceGenerationOptions options) - { - var builder = new SourceBuilder() - .WriteLine(GeneratedFilesHeader) - .WriteLine("using System;") - .WriteLine() - .WriteLine($"namespace {RootNamespace}") - .WriteOpeningBracket(); - - if (options.GenerateXmlDocument) - { - builder - .WriteLine("/// ") - .WriteLine("/// Specifies that the annotated property should be excluded.") - .WriteLine("/// "); - } - - builder - .WriteLine("[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false, AllowMultiple = false)]") - .WriteLine($"public sealed class {AttributeClassName} : Attribute {{ }}") - .WriteClosingBracket(); - - return new(builder.ToString(), $"{AttributeClassName}.g.cs"); - } - } -} \ No newline at end of file diff --git a/src/BlueWest.MapTo/Sources/JsonExtensionAttributeSource.cs b/src/BlueWest.MapTo/Sources/JsonExtensionAttributeSource.cs deleted file mode 100644 index 6d86dde..0000000 --- a/src/BlueWest.MapTo/Sources/JsonExtensionAttributeSource.cs +++ /dev/null @@ -1,40 +0,0 @@ -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("/// ") - .WriteLine("/// Specifies that the annotated class has a json extension.") - .WriteLine("/// "); - } - - 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"); - } - } -} \ No newline at end of file diff --git a/src/BlueWest.MapTo/Sources/MapClassSource.cs b/src/BlueWest.MapTo/Sources/MapClassSource.cs deleted file mode 100644 index 9278fe6..0000000 --- a/src/BlueWest.MapTo/Sources/MapClassSource.cs +++ /dev/null @@ -1,14 +0,0 @@ -using MapTo.Extensions; -using System.Text; -using static MapTo.Sources.Constants; - -namespace MapTo.Sources -{ - internal static class MapClassSource - { - internal static SourceCode Generate(MappingModel model) - { - return model.GenerateStructOrClass("class"); - } - } -} \ No newline at end of file diff --git a/src/BlueWest.MapTo/Sources/MapFromAttributeSource.cs b/src/BlueWest.MapTo/Sources/MapFromAttributeSource.cs deleted file mode 100644 index c250245..0000000 --- a/src/BlueWest.MapTo/Sources/MapFromAttributeSource.cs +++ /dev/null @@ -1,72 +0,0 @@ -using static MapTo.Sources.Constants; - -namespace MapTo.Sources -{ - internal static class MapFromAttributeSource - { - internal const string AttributeName = "MapFrom"; - 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("/// ") - .WriteLine("/// Specifies that the annotated class can be mapped from the provided .") - .WriteLine("/// "); - } - - builder - .WriteLine("[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]") - .WriteLine($"public sealed class {AttributeName}Attribute : Attribute") - .WriteOpeningBracket(); - - if (options.GenerateXmlDocument) - { - builder - .WriteLine("/// ") - .WriteLine($"/// Initializes a new instance of the class with the specified .") - .WriteLine("/// ") - .WriteLine("/// The type of to map from."); - } - - builder - .WriteLine($"public {AttributeName}Attribute(Type sourceType)") - .WriteOpeningBracket() - .WriteLine("SourceType = new [] { sourceType };") - .WriteClosingBracket() - .WriteLine(); - - builder - .WriteLine($"public {AttributeName}Attribute(Type[] sourceType)") - .WriteOpeningBracket() - .WriteLine("SourceType = sourceType;") - .WriteClosingBracket() - .WriteLine(); - - if (options.GenerateXmlDocument) - { - builder - .WriteLine("/// ") - .WriteLine("/// Gets the type to map from.") - .WriteLine("/// "); - } - - builder - .WriteLine("public Type[] SourceType { get; }") - .WriteClosingBracket() // class - .WriteClosingBracket(); // namespace - - return new(builder.ToString(), $"{AttributeName}Attribute.g.cs"); - } - } -} \ No newline at end of file diff --git a/src/BlueWest.MapTo/Sources/MapPropertyAttributeSource.cs b/src/BlueWest.MapTo/Sources/MapPropertyAttributeSource.cs deleted file mode 100644 index e817140..0000000 --- a/src/BlueWest.MapTo/Sources/MapPropertyAttributeSource.cs +++ /dev/null @@ -1,61 +0,0 @@ -using static MapTo.Sources.Constants; - -namespace MapTo.Sources -{ - internal static class MapPropertyAttributeSource - { - internal const string AttributeName = "MapProperty"; - internal const string AttributeClassName = AttributeName + "Attribute"; - internal const string FullyQualifiedName = RootNamespace + "." + AttributeClassName; - internal const string SourcePropertyNamePropertyName = "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() - .WriteLine("/// ") - .WriteLine("/// Specifies the mapping behavior of the annotated property.") - .WriteLine("/// ") - .WriteLine("/// ") - .WriteLine($"/// {AttributeClassName} has a number of uses:") - .WriteLine("/// ") - .WriteLine("/// By default properties with same name will get mapped. This attribute allows the names to be different.") - .WriteLine("/// Indicates that a property should be mapped when member serialization is set to opt-in.") - .WriteLine("/// ") - .WriteLine("/// "); - } - - builder - .WriteLine("[AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = true)]") - .WriteLine($"public sealed class {AttributeClassName} : Attribute") - .WriteOpeningBracket(); - - if (options.GenerateXmlDocument) - { - builder - .WriteLine("/// ") - .WriteLine("/// Gets or sets the property name of the object to mapping from.") - .WriteLine("/// "); - } - - builder - .WriteLine($"public string{options.NullableReferenceSyntax} {SourcePropertyNamePropertyName} {{ get; set; }}") - .WriteClosingBracket() // class - .WriteClosingBracket(); // namespace - - - return new(builder.ToString(), $"{AttributeClassName}.g.cs"); - } - } -} \ No newline at end of file diff --git a/src/BlueWest.MapTo/Sources/MapRecordSource.cs b/src/BlueWest.MapTo/Sources/MapRecordSource.cs deleted file mode 100644 index 97b67de..0000000 --- a/src/BlueWest.MapTo/Sources/MapRecordSource.cs +++ /dev/null @@ -1,235 +0,0 @@ -using System; -using MapTo.Extensions; -using static MapTo.Sources.Constants; - -namespace MapTo.Sources -{ - internal static class MapRecordSource - { - internal static SourceCode Generate(MappingModel model) - { - using var builder = new SourceBuilder() - .WriteLine(GeneratedFilesHeader) - .WriteNullableContextOptionIf(model.Options.SupportNullableReferenceTypes) - .WriteUsings(model.Usings) - .WriteLine() - - // Namespace declaration - .WriteLine($"namespace {model.Namespace}") - .WriteOpeningBracket() - - // Class declaration - .WriteLine($"partial record {model.TypeIdentifierName}") - .WriteOpeningBracket(); - - foreach (var targetSourceType in model.MappedSourceTypes) - { - if (targetSourceType.GenerateSecondaryConstructor) - { - builder - .GenerateSecondaryConstructor(model) - .WriteLine(); - } - } - - // Class body - - - builder - .GeneratePrivateConstructor(model) - - .WriteLine() - .GenerateFactoryMethod(model) - - // End class declaration - .WriteClosingBracket() - .WriteLine() - - // Extension class declaration - .GenerateSourceTypeExtensionClass(model) - - - // End namespace declaration - .WriteClosingBracket(); - - return new(builder.ToString(), $"{model.Namespace}.{model.TypeIdentifierName}.g.cs"); - } - - private static SourceBuilder GenerateSecondaryConstructor(this SourceBuilder builder, MappingModel model) - { - // grab first data from array - - foreach (var targetSourceType in model.MappedSourceTypes) - { - var sourceClassParameterName = targetSourceType.SourceTypeIdentifierName.ToCamelCase(); - if (model.Options.GenerateXmlDocument) - { - builder - .WriteLine("/// ") - .WriteLine($"/// Initializes a new instance of the class") - .WriteLine($"/// using the property values from the specified .") - .WriteLine("/// ") - .WriteLine($"/// {sourceClassParameterName} is null"); - } - builder .WriteLine($"{model.Options.ConstructorAccessModifier.ToLowercaseString()} {model.TypeIdentifierName}({targetSourceType.SourceType} {sourceClassParameterName})") - .WriteLine($" : this(new {MappingContextSource.ClassName}(), {sourceClassParameterName}) {{ }}"); - } - - - - return builder; - - } - - private static SourceBuilder GeneratePrivateConstructor(this SourceBuilder builder, MappingModel model) - { - const string mappingContextParameterName = "context"; - - foreach (var targetSourceType in model.MappedSourceTypes) - { - var sourceClassParameterName = targetSourceType.SourceTypeIdentifierName.ToCamelCase(); - builder - .WriteLine( - $"private protected {model.TypeIdentifierName}({MappingContextSource.ClassName} {mappingContextParameterName}, {targetSourceType.SourceType} {sourceClassParameterName})") - .Indent() - .Write(": this(").WriteProperties(model, sourceClassParameterName, mappingContextParameterName) - .WriteLine(")") - .Unindent() - .WriteOpeningBracket() - .WriteLine($"if ({mappingContextParameterName} == null) throw new ArgumentNullException(nameof({mappingContextParameterName}));") - .WriteLine($"if ({sourceClassParameterName} == null) throw new ArgumentNullException(nameof({sourceClassParameterName}));") - .WriteLine() - .WriteLine($"{mappingContextParameterName}.{MappingContextSource.RegisterMethodName}({sourceClassParameterName}, this);") - .WriteClosingBracket(); - } - // End constructor declaration - return builder; - } - - private static SourceBuilder WriteProperties(this SourceBuilder builder, MappingModel model, string sourceClassParameterName, - string mappingContextParameterName) - { - - foreach (var targetSourceType in model.MappedSourceTypes) - { - for (var i = 0; i < targetSourceType.SourceProperties.Length; i++) - { - var property = targetSourceType.SourceProperties[i]; - if (property.TypeConverter is null) - { - if (property.IsEnumerable) - { - builder.Write( - $"{property.Name}: {sourceClassParameterName}.{property.SourcePropertyName}.Select({mappingContextParameterName}.{MappingContextSource.MapMethodName}<{property.MappedSourcePropertyTypeName}, {property.EnumerableTypeArgument}>).ToList()"); - } - else - { - builder.Write(property.MappedSourcePropertyTypeName is null - ? $"{property.Name}: {sourceClassParameterName}.{property.SourcePropertyName}" - : $"{property.Name}: {mappingContextParameterName}.{MappingContextSource.MapMethodName}<{property.MappedSourcePropertyTypeName}, {property.Type}>({sourceClassParameterName}.{property.SourcePropertyName})"); - } - } - else - { - var parameters = property.TypeConverterParameters.IsEmpty - ? "null" - : $"new object[] {{ {string.Join(", ", property.TypeConverterParameters)} }}"; - - builder.Write( - $"{property.Name}: new {property.TypeConverter}().Convert({sourceClassParameterName}.{property.SourcePropertyName}, {parameters})"); - } - - if (i < targetSourceType.SourceProperties.Length - 1) - { - builder.Write(", "); - } - } - } - - - - return builder; - } - - private static SourceBuilder GenerateFactoryMethod(this SourceBuilder builder, MappingModel model) - { - foreach (var targetSourceType in model.MappedSourceTypes) - { - var sourceClassParameterName = targetSourceType.SourceTypeIdentifierName.ToCamelCase(); - - builder - .GenerateConvertorMethodsXmlDocs(model, sourceClassParameterName) - .WriteLineIf(model.Options.SupportNullableStaticAnalysis, $"[return: NotNullIfNotNull(\"{sourceClassParameterName}\")]") - .WriteLine( - $"{model.Options.GeneratedMethodsAccessModifier.ToLowercaseString()} static {model.TypeIdentifierName}{model.Options.NullableReferenceSyntax} From({targetSourceType.SourceType}{model.Options.NullableReferenceSyntax} {sourceClassParameterName})") - .WriteOpeningBracket() - .WriteLine( - $"return {sourceClassParameterName} == null ? null : {MappingContextSource.ClassName}.{MappingContextSource.FactoryMethodName}<{targetSourceType.SourceType}, {model.TypeIdentifierName}>({sourceClassParameterName});") - .WriteClosingBracket(); - } - - - return builder; - } - - private static SourceBuilder GenerateConvertorMethodsXmlDocs(this SourceBuilder builder, MappingModel model, string sourceClassParameterName) - { - if (!model.Options.GenerateXmlDocument) - { - return builder; - } - - foreach (var targetSourceType in model.MappedSourceTypes) - { - builder - .WriteLine("/// ") - .WriteLine($"/// Creates a new instance of and sets its participating properties") - .WriteLine($"/// using the property values from .") - .WriteLine("/// ") - .WriteLine($"/// The instance of to use as source.") - .WriteLine( - $"/// A new instance of -or- null if is null."); - } - - return builder; - - } - - private static SourceBuilder GenerateSourceTypeExtensionClass(this SourceBuilder builder, MappingModel model) - { - foreach (var targetSourceType in model.MappedSourceTypes) - { - builder - .WriteLine( - $"{model.Options.GeneratedMethodsAccessModifier.ToLowercaseString()} static partial class {targetSourceType.SourceTypeIdentifierName}To{model.TypeIdentifierName}Extensions") - .WriteOpeningBracket() - .GenerateSourceTypeExtensionMethod(model) - .WriteClosingBracket(); - } - - return builder; - } - - private static SourceBuilder GenerateSourceTypeExtensionMethod(this SourceBuilder builder, MappingModel model) - { - - foreach (var targetSourceType in model.MappedSourceTypes) - { - var sourceClassParameterName = targetSourceType.SourceTypeIdentifierName.ToCamelCase(); - - builder - .GenerateConvertorMethodsXmlDocs(model, sourceClassParameterName) - .WriteLineIf(model.Options.SupportNullableStaticAnalysis, $"[return: NotNullIfNotNull(\"{sourceClassParameterName}\")]") - .WriteLine( - $"{model.Options.GeneratedMethodsAccessModifier.ToLowercaseString()} static {model.TypeIdentifierName}{model.Options.NullableReferenceSyntax} To{model.TypeIdentifierName}(this {targetSourceType.SourceType}{model.Options.NullableReferenceSyntax} {sourceClassParameterName})") - .WriteOpeningBracket() - .WriteLine($"return {sourceClassParameterName} == null ? null : new {model.TypeIdentifierName}({sourceClassParameterName});") - .WriteClosingBracket(); - - } - - - return builder; - } - } -} \ No newline at end of file diff --git a/src/BlueWest.MapTo/Sources/MapStructSource.cs b/src/BlueWest.MapTo/Sources/MapStructSource.cs deleted file mode 100644 index 32f4960..0000000 --- a/src/BlueWest.MapTo/Sources/MapStructSource.cs +++ /dev/null @@ -1,15 +0,0 @@ -using MapTo.Extensions; -using static MapTo.Sources.Constants; -using System.Collections.Generic; -using System.Text; - -namespace MapTo.Sources -{ - internal static class MapStructSource - { - internal static SourceCode Generate(MappingModel model) - { - return model.GenerateStructOrClass("struct"); - } - } -} \ No newline at end of file diff --git a/src/BlueWest.MapTo/Sources/MapTypeConverterAttributeSource.cs b/src/BlueWest.MapTo/Sources/MapTypeConverterAttributeSource.cs deleted file mode 100644 index f9efa26..0000000 --- a/src/BlueWest.MapTo/Sources/MapTypeConverterAttributeSource.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using static MapTo.Sources.Constants; - -namespace MapTo.Sources -{ - internal static class MapTypeConverterAttributeSource - { - internal const string AttributeName = "MapTypeConverter"; - internal const string AttributeClassName = AttributeName + "Attribute"; - internal const string FullyQualifiedName = RootNamespace + "." + AttributeClassName; - internal const string ConverterPropertyName = "Converter"; - internal const string ConverterParametersPropertyName = "ConverterParameters"; - - 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("/// ") - .WriteLine("/// Specifies what type to use as a converter for the property this attribute is bound to.") - .WriteLine("/// "); - } - - builder - .WriteLine("[AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false)]") - .WriteLine($"public sealed class {AttributeClassName} : Attribute") - .WriteOpeningBracket(); - - if (options.GenerateXmlDocument) - { - builder - .WriteLine("/// ") - .WriteLine($"/// Initializes a new instance of .") - .WriteLine("/// ") - .WriteLine($"/// The to be used to convert the source type.") - .WriteLine("/// The list of parameters to pass to the during the type conversion."); - } - - builder - .WriteLine($"public {AttributeClassName}(Type converter, object[]{options.NullableReferenceSyntax} converterParameters = null)") - .WriteOpeningBracket() - .WriteLine($"{ConverterPropertyName} = converter;") - .WriteLine($"{ConverterParametersPropertyName} = converterParameters;") - .WriteClosingBracket() - .WriteLine(); - - if (options.GenerateXmlDocument) - { - builder - .WriteLine("/// ") - .WriteLine($"/// Gets or sets the to be used to convert the source type.") - .WriteLine("/// "); - } - - builder - .WriteLine($"public Type {ConverterPropertyName} {{ get; }}") - .WriteLine(); - - if (options.GenerateXmlDocument) - { - builder - .WriteLine("/// ") - .WriteLine($"/// Gets the list of parameters to pass to the during the type conversion.") - .WriteLine("/// "); - } - - builder - .WriteLine($"public object[]{options.NullableReferenceSyntax} {ConverterParametersPropertyName} {{ get; }}") - .WriteClosingBracket() - .WriteClosingBracket(); - - return new(builder.ToString(), $"{AttributeClassName}.g.cs"); - } - } -} \ No newline at end of file diff --git a/src/BlueWest.MapTo/Sources/MappingContextSource.cs b/src/BlueWest.MapTo/Sources/MappingContextSource.cs deleted file mode 100644 index 76b6286..0000000 --- a/src/BlueWest.MapTo/Sources/MappingContextSource.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System.Collections.Generic; -using static MapTo.Sources.Constants; - -namespace MapTo.Sources -{ - internal static class MappingContextSource - { - internal const string ClassName = "MappingContext"; - internal const string FullyQualifiedName = RootNamespace + "." + ClassName; - internal const string FactoryMethodName = "Create"; - internal const string RegisterMethodName = "Register"; - internal const string MapMethodName = "MapFromWithContext"; - - internal static SourceCode Generate(SourceGenerationOptions options) - { - var usings = new List { "System", "System.Collections.Generic", "System.Reflection" }; - - using var builder = new SourceBuilder() - .WriteLine(GeneratedFilesHeader) - .WriteLine() - .WriteUsings(usings) - .WriteLine() - - // Namespace declaration - .WriteLine($"namespace {RootNamespace}") - .WriteOpeningBracket() - - // Class declaration - .WriteLine($"internal sealed class {ClassName}") - .WriteOpeningBracket() - - .WriteLine("private readonly Dictionary _cache;") - .WriteLine() - - // Constructor - .WriteLine($"internal {ClassName}()") - .WriteOpeningBracket() - .WriteLine("_cache = new Dictionary(1);") - .WriteClosingBracket() - .WriteLine() - - // Factory - .WriteLine($"internal static TMapped {FactoryMethodName}(TOriginal original)") - .WriteOpeningBracket() - .WriteLine("if (original == null) throw new ArgumentNullException(nameof(original));") - .WriteLine() - .WriteLine("var context = new MappingContext();") - .WriteLine("var mapped = context.MapFromWithContext(original);") - .WriteLine() - .WriteLine("if (mapped == null)") - .WriteOpeningBracket() - .WriteLine("throw new InvalidOperationException();") - .WriteClosingBracket() - .WriteLine() - .WriteLine("return mapped;") - .WriteClosingBracket() - .WriteLine() - - // MapFromWithContext method - .WriteLine($"internal TMapped MapFromWithContext(TOriginal original)") - .WriteOpeningBracket() - .WriteLine("if (original == null)") - .WriteOpeningBracket() - .WriteLine("return default(TMapped);") - .WriteClosingBracket() - .WriteLine() - .WriteLine("if (!TryGetValue(original, out var mapped))") - .WriteOpeningBracket() - .WriteLine("var instance = Activator.CreateInstance(typeof(TMapped), BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { this, original }, null);") - .WriteLine("if (instance != null)") - .WriteOpeningBracket() - .WriteLine("mapped = (TMapped)instance;") - .WriteClosingBracket() - .WriteClosingBracket() - .WriteLine() - .WriteLine("return mapped;") - .WriteClosingBracket() - .WriteLine() - - // Register method - .WriteLine("internal void Register(TOriginal original, TMapped mapped)") - .WriteOpeningBracket() - .WriteLine("if (original == null) throw new ArgumentNullException(nameof(original));") - .WriteLine("if (mapped == null) throw new ArgumentNullException(nameof(mapped));") - .WriteLine() - .WriteLine("if (!_cache.ContainsKey(original))") - .WriteOpeningBracket() - .WriteLine("_cache.Add(original, mapped);") - .WriteClosingBracket() - .WriteClosingBracket() - .WriteLine() - - // TryGetValue method - .WriteLine("private bool TryGetValue(TOriginal original, out TMapped mapped)") - .WriteOpeningBracket() - .WriteLine("if (original != null && _cache.TryGetValue(original, out var value))") - .WriteOpeningBracket() - .WriteLine("mapped = (TMapped)value;") - .WriteLine("return true;") - .WriteClosingBracket() - .WriteLine() - .WriteLine("mapped = default(TMapped);") - .WriteLine("return false;") - .WriteClosingBracket() - - // End class declaration - .WriteClosingBracket() - - // End namespace declaration - .WriteClosingBracket(); - - return new(builder.ToString(), $"{ClassName}.g.cs"); - } - } -} \ No newline at end of file diff --git a/src/BlueWest.MapTo/Sources/ReadOnlyPropertyAttributeSource.cs b/src/BlueWest.MapTo/Sources/ReadOnlyPropertyAttributeSource.cs deleted file mode 100644 index 3f0b2c5..0000000 --- a/src/BlueWest.MapTo/Sources/ReadOnlyPropertyAttributeSource.cs +++ /dev/null @@ -1,36 +0,0 @@ -using static MapTo.Sources.Constants; - -namespace MapTo.Sources -{ - internal static class ReadOnlyPropertyAttributeSource - { - internal const string AttributeName = "ReadOnlyProperty"; - internal const string AttributeClassName = AttributeName + "Attribute"; - internal const string FullyQualifiedName = RootNamespace + "." + AttributeClassName; - - internal static SourceCode Generate(SourceGenerationOptions options) - { - var builder = new SourceBuilder() - .WriteLine(GeneratedFilesHeader) - .WriteLine("using System;") - .WriteLine() - .WriteLine($"namespace {RootNamespace}") - .WriteOpeningBracket(); - - if (options.GenerateXmlDocument) - { - builder - .WriteLine("/// ") - .WriteLine("/// Specifies that the annotated property should be excluded.") - .WriteLine("/// "); - } - - builder - .WriteLine("[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]") - .WriteLine($"public sealed class {AttributeClassName} : Attribute {{ }}") - .WriteClosingBracket(); - - return new(builder.ToString(), $"{AttributeClassName}.g.cs"); - } - } -} \ No newline at end of file diff --git a/src/BlueWest.MapTo/Sources/UseUpdateAttributeSource.cs b/src/BlueWest.MapTo/Sources/UseUpdateAttributeSource.cs deleted file mode 100644 index dae6d71..0000000 --- a/src/BlueWest.MapTo/Sources/UseUpdateAttributeSource.cs +++ /dev/null @@ -1,40 +0,0 @@ -using static MapTo.Sources.Constants; - -namespace MapTo.Sources -{ - internal static class UseUpdateAttributeSource - { - internal const string AttributeName = "UseUpdate"; - 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("/// ") - .WriteLine("/// Specifies that the annotated class can be updatable.") - .WriteLine("/// "); - } - - 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"); - } - } -} \ No newline at end of file diff --git a/src/BlueWest.MapTo/StructMappingContext.cs b/src/BlueWest.MapTo/StructMappingContext.cs deleted file mode 100644 index 8dd73a0..0000000 --- a/src/BlueWest.MapTo/StructMappingContext.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Collections.Immutable; -using System.Linq; -using MapTo.Extensions; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace MapTo -{ - internal class StructMappingContext : MappingContext - { - internal StructMappingContext(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, TypeDeclarationSyntax typeSyntax) - : base(compilation, sourceGenerationOptions, typeSyntax) { } - - protected override ImmutableArray GetSourceMappedFields(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass) - { - var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType().ToArray(); - - return typeSymbol - .GetAllMembers() - .OfType() - .Where(p => !p.HasAttribute(IgnoreMemberAttributeTypeSymbol)) - .Select(property => MapField(sourceTypeSymbol, sourceProperties, property)) - .Where(mappedProperty => mappedProperty is not null) - .ToImmutableArray()!; - } - - protected override ImmutableArray GetSourceMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool hasInheritedClass) - { - var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType().ToArray(); - - return typeSymbol - .GetAllMembers() - .OfType() - .Where(p => !p.HasAttribute(IgnoreMemberAttributeTypeSymbol)) - .Select(property => MapProperty(sourceTypeSymbol, sourceProperties, property)) - .Where(mappedProperty => mappedProperty is not null) - .ToImmutableArray()!; - } - - protected override ImmutableArray GetTypeMappedFields(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool isInheritFromMappedBaseClass) - { - var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType().ToArray(); - - return sourceTypeSymbol - .GetAllMembers() - .OfType() - .Where(p => !p.HasAttribute(IgnoreMemberAttributeTypeSymbol)) - .Select(property => MapFieldSimple(typeSymbol, property)) - .Where(mappedProperty => mappedProperty is not null) - .ToImmutableArray()!; - } - - protected override ImmutableArray GetTypeMappedProperties(ITypeSymbol typeSymbol, ITypeSymbol sourceTypeSymbol, bool hasInheritedClass) - { - var sourceProperties = sourceTypeSymbol.GetAllMembers().OfType().ToArray(); - - return sourceTypeSymbol - .GetAllMembers() - .OfType() - .Where(p => !p.HasAttribute(IgnoreMemberAttributeTypeSymbol)) - .Select(property => MapPropertySimple(typeSymbol, property)) - .Where(mappedProperty => mappedProperty is not null) - .ToImmutableArray()!; - } - - } -} \ No newline at end of file diff --git a/test/BlueWest.MapTo.Tests/BlueWest.MapTo.Tests.csproj b/test/BlueWest.MapTo.Tests/BlueWest.MapTo.Tests.csproj deleted file mode 100644 index 8603727..0000000 --- a/test/BlueWest.MapTo.Tests/BlueWest.MapTo.Tests.csproj +++ /dev/null @@ -1,42 +0,0 @@ - - - - net6.0 - - false - - enable - - MapTo.Tests - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - 3.5.109 - - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - diff --git a/test/BlueWest.MapTo.Tests/Common.cs b/test/BlueWest.MapTo.Tests/Common.cs deleted file mode 100644 index f9113c9..0000000 --- a/test/BlueWest.MapTo.Tests/Common.cs +++ /dev/null @@ -1,252 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using MapTo.Extensions; -using MapTo.Sources; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Shouldly; - -namespace MapTo.Tests -{ - internal static class Common - { - internal const int Indent1 = 4; - internal const int Indent2 = Indent1 * 2; - internal const int Indent3 = Indent1 * 3; - internal static readonly Location IgnoreLocation = Location.None; - - internal static readonly Dictionary DefaultAnalyzerOptions = new() - { - [GeneratorExecutionContextExtensions.GetBuildPropertyName(nameof(SourceGenerationOptions.GenerateXmlDocument))] = "false" - }; - - internal static string GetSourceText(SourceGeneratorOptions? options = null) - { - const string ns = "Test"; - options ??= new SourceGeneratorOptions(); - var hasDifferentSourceNamespace = options.SourceClassNamespace != ns; - var builder = new SourceBuilder(); - - builder.WriteLine("//"); - builder.WriteLine("// Test source code."); - builder.WriteLine("//"); - builder.WriteLine(); - - options.Usings?.ForEach(s => builder.WriteLine($"using {s};")); - - if (options.UseMapToNamespace) - { - builder.WriteLine($"using {Constants.RootNamespace};"); - } - - builder - .WriteLine($"using {options.SourceClassNamespace};") - .WriteLine() - .WriteLine(); - - builder - .WriteLine($"namespace {ns}") - .WriteOpeningBracket(); - - if (hasDifferentSourceNamespace && options.UseMapToNamespace) - { - builder - .WriteLine($"using {options.SourceClassNamespace};") - .WriteLine() - .WriteLine(); - } - - builder - .WriteLine(options.UseMapToNamespace ? "[MapFrom(typeof(Baz))]" : "[MapTo.MapFrom(typeof(Baz))]") - .WriteLine("public partial class Foo") - .WriteOpeningBracket(); - - for (var i = 1; i <= options.ClassPropertiesCount; i++) - { - builder.WriteLine(i % 2 == 0 ? $"public int Prop{i} {{ get; set; }}" : $"public int Prop{i} {{ get; }}"); - } - - options.PropertyBuilder?.Invoke(builder); - - builder - .WriteClosingBracket() - .WriteClosingBracket() - .WriteLine() - .WriteLine(); - - builder - .WriteLine($"namespace {options.SourceClassNamespace}") - .WriteOpeningBracket() - .WriteLine("public class Baz") - .WriteOpeningBracket(); - - for (var i = 1; i <= options.SourceClassPropertiesCount; i++) - { - builder.WriteLine(i % 2 == 0 ? $"public int Prop{i} {{ get; set; }}" : $"public int Prop{i} {{ get; }}"); - } - - options.SourcePropertyBuilder?.Invoke(builder); - - builder - .WriteClosingBracket() - .WriteClosingBracket(); - - return builder.ToString(); - } - - internal static string[] GetEmployeeManagerSourceText( - Func? employeeClassSource = null, - Func? managerClassSource = null, - Func? employeeViewModelSource = null, - Func? managerViewModelSource = null, - bool useDifferentViewModelNamespace = false) - { - return new[] - { - employeeClassSource?.Invoke() ?? DefaultEmployeeClassSource(), - managerClassSource?.Invoke() ?? DefaultManagerClassSource(), - employeeViewModelSource?.Invoke() ?? - DefaultEmployeeViewModelSource(useDifferentViewModelNamespace), - managerViewModelSource?.Invoke() ?? DefaultManagerViewModelSource(useDifferentViewModelNamespace) - }; - - static string DefaultEmployeeClassSource() => - @" -using System; -using System.Collections.Generic; -using System.Text; - -namespace Test.Data.Models -{ - public class Employee - { - public int Id { get; set; } - - public string EmployeeCode { get; set; } - - public Manager Manager { get; set; } - } -}".Trim(); - - static string DefaultManagerClassSource() => - @"using System; -using System.Collections.Generic; -using System.Text; - -namespace Test.Data.Models -{ - public class Manager: Employee - { - public int Level { get; set; } - - public IEnumerable Employees { get; set; } = Array.Empty(); - } -} -".Trim(); - - static string DefaultEmployeeViewModelSource(bool useDifferentNamespace) => useDifferentNamespace - ? @" -using MapTo; -using Test.Data.Models; -using Test.ViewModels2; - -namespace Test.ViewModels -{ - [MapFrom(typeof(Employee))] - public partial class EmployeeViewModel - { - public int Id { get; set; } - - public string EmployeeCode { get; set; } - - public ManagerViewModel Manager { get; set; } - } -} -".Trim() - : @" -using MapTo; -using Test.Data.Models; - -namespace Test.ViewModels -{ - [MapFrom(typeof(Employee))] - public partial class EmployeeViewModel - { - public int Id { get; set; } - - public string EmployeeCode { get; set; } - - public ManagerViewModel Manager { get; set; } - } -} -".Trim(); - - static string DefaultManagerViewModelSource(bool useDifferentNamespace) => useDifferentNamespace - ? @" -using System; -using System.Collections.Generic; -using MapTo; -using Test.Data.Models; -using Test.ViewModels; - -namespace Test.ViewModels2 -{ - [MapFrom(typeof(Manager))] - public partial class ManagerViewModel : EmployeeViewModel - { - public int Level { get; set; } - - public IEnumerable Employees { get; set; } = Array.Empty(); - } -} -".Trim() - : @" -using System; -using System.Collections.Generic; -using MapTo; -using Test.Data.Models; - -namespace Test.ViewModels -{ - [MapFrom(typeof(Manager))] - public partial class ManagerViewModel : EmployeeViewModel - { - public int Level { get; set; } - - public IEnumerable Employees { get; set; } = Array.Empty(); - } -}".Trim(); - } - - internal static PropertyDeclarationSyntax GetPropertyDeclarationSyntax(SyntaxTree syntaxTree, string targetPropertyName, string targetClass = "Foo") - { - return syntaxTree.GetRoot() - .DescendantNodes() - .OfType() - .Single(c => c.Identifier.ValueText == targetClass) - .DescendantNodes() - .OfType() - .Single(p => p.Identifier.ValueText == targetPropertyName); - } - - internal static IPropertySymbol GetSourcePropertySymbol(string propertyName, Compilation compilation, string targetClass = "Foo") - { - var syntaxTree = compilation.SyntaxTrees.First(); - var propSyntax = GetPropertyDeclarationSyntax(syntaxTree, propertyName, targetClass); - - var semanticModel = compilation.GetSemanticModel(syntaxTree); - return semanticModel.GetDeclaredSymbol(propSyntax).ShouldNotBeNull(); - } - - internal record SourceGeneratorOptions( - bool UseMapToNamespace = false, - string SourceClassNamespace = "Test.Models", - int ClassPropertiesCount = 3, - int SourceClassPropertiesCount = 3, - Action? PropertyBuilder = null, - Action? SourcePropertyBuilder = null, - IEnumerable? Usings = null); - } -} \ No newline at end of file diff --git a/test/BlueWest.MapTo.Tests/CompilerServices/IsExternalInit.cs b/test/BlueWest.MapTo.Tests/CompilerServices/IsExternalInit.cs deleted file mode 100644 index e750e2f..0000000 --- a/test/BlueWest.MapTo.Tests/CompilerServices/IsExternalInit.cs +++ /dev/null @@ -1,16 +0,0 @@ -// ReSharper disable UnusedType.Global -// ReSharper disable CheckNamespace -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.ComponentModel; - -namespace System.Runtime.CompilerServices -{ - /// - /// Reserved to be used by the compiler for tracking metadata. - /// This class should not be used by developers in source code. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - internal static class IsExternalInit { } -} \ No newline at end of file diff --git a/test/BlueWest.MapTo.Tests/Extensions/RoslynExtensions.cs b/test/BlueWest.MapTo.Tests/Extensions/RoslynExtensions.cs deleted file mode 100644 index 24b24d7..0000000 --- a/test/BlueWest.MapTo.Tests/Extensions/RoslynExtensions.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis; - -namespace MapTo.Tests.Extensions -{ - internal static class RoslynExtensions - { - internal static SyntaxTree? GetGeneratedSyntaxTree(this Compilation compilation, string className) => - compilation.SyntaxTrees.FirstOrDefault(s => s.FilePath.EndsWith($"{className}.g.cs")); - - internal static string PrintSyntaxTree(this Compilation compilation) - { - var builder = new StringBuilder(); - - return string.Join( - Environment.NewLine, - compilation.SyntaxTrees - .Reverse() - .Select((s, i) => - { - builder - .Clear() - .AppendLine("----------------------------------------") - .AppendFormat("File Path: \"{0}\"", s.FilePath).AppendLine() - .AppendFormat("Index: \"{0}\"", i).AppendLine() - .AppendLine(); - - var lines = s.ToString().Split(Environment.NewLine); - var lineNumber = 0; - foreach (var line in lines) - { - builder.AppendFormat("{0:00}: {1}", lineNumber, line).AppendLine(); - lineNumber++; - } - - return builder.ToString(); - })); - } - } -} \ No newline at end of file diff --git a/test/BlueWest.MapTo.Tests/Extensions/ShouldlyExtensions.cs b/test/BlueWest.MapTo.Tests/Extensions/ShouldlyExtensions.cs deleted file mode 100644 index 233432a..0000000 --- a/test/BlueWest.MapTo.Tests/Extensions/ShouldlyExtensions.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis; -using Shouldly; -using Xunit; - -namespace MapTo.Tests.Extensions -{ - internal static class ShouldlyExtensions - { - internal static void ShouldContainSource(this IEnumerable syntaxTree, string typeName, string expectedSource, string? customMessage = null) - { - var syntax = syntaxTree - .Select(s => s.ToString().Trim()) - .FirstOrDefault(s => s.Contains(typeName)); - - syntax.ShouldNotBeNullOrWhiteSpace(); - syntax.ShouldBe(expectedSource, customMessage); - } - - internal static void ShouldContainPartialSource(this IEnumerable syntaxTree, string typeName, string expectedSource, string? customMessage = null) - { - var syntax = syntaxTree - .Select(s => s.ToString().Trim()) - .FirstOrDefault(s => s.Contains(typeName)); - - syntax.ShouldNotBeNullOrWhiteSpace(); - syntax.ShouldContainWithoutWhitespace(expectedSource, customMessage); - } - - internal static void ShouldContainPartialSource(this SyntaxTree syntaxTree, string expectedSource, string? customMessage = null) - { - var syntax = syntaxTree.ToString(); - syntax.ShouldNotBeNullOrWhiteSpace(); - syntax.ShouldContainWithoutWhitespace(expectedSource, customMessage); - } - - internal static void ShouldBeSuccessful(this IEnumerable diagnostics, Compilation? compilation = null, IEnumerable? ignoreDiagnosticsIds = null) - { - var actual = diagnostics - .Where(d => (ignoreDiagnosticsIds is null || ignoreDiagnosticsIds.All(i => !d.Id.StartsWith(i) )) && (d.Severity is DiagnosticSeverity.Warning or DiagnosticSeverity.Error)) - .Select(c => $"{c.Severity}: {c.Location.GetLineSpan()} - {c.GetMessage()}").ToArray(); - - if (!actual.Any()) - { - return; - } - - var builder = new StringBuilder(); - builder.AppendLine("Failed"); - - foreach (var d in actual) - { - builder.AppendFormat("- {0}", d).AppendLine(); - } - - if (compilation is not null) - { - builder.AppendLine("Generated Sources:"); - builder.AppendLine(compilation.PrintSyntaxTree()); - } - - Assert.False(true, builder.ToString()); - } - - internal static void ShouldNotBeSuccessful(this ImmutableArray diagnostics, Diagnostic expectedError) - { - var actualDiagnostics = diagnostics.FirstOrDefault(d => d.Id == expectedError.Id); - var compilationDiagnostics = actualDiagnostics == null ? diagnostics : diagnostics.Except(new[] { actualDiagnostics }); - - compilationDiagnostics.ShouldBeSuccessful(); - - Assert.NotNull(actualDiagnostics); - Assert.Equal(expectedError.Id, actualDiagnostics?.Id); - Assert.Equal(expectedError.Descriptor.Id, actualDiagnostics?.Descriptor.Id); - Assert.Equal(expectedError.Descriptor.Description, actualDiagnostics?.Descriptor.Description); - Assert.Equal(expectedError.Descriptor.Title, actualDiagnostics?.Descriptor.Title); - - if (expectedError.Location != Location.None) - { - Assert.Equal(expectedError.Location, actualDiagnostics?.Location); - } - } - } -} \ No newline at end of file diff --git a/test/BlueWest.MapTo.Tests/IgnorePropertyAttributeTests.cs b/test/BlueWest.MapTo.Tests/IgnorePropertyAttributeTests.cs deleted file mode 100644 index 237afa9..0000000 --- a/test/BlueWest.MapTo.Tests/IgnorePropertyAttributeTests.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Linq; -using MapTo.Extensions; -using MapTo.Sources; -using MapTo.Tests.Extensions; -using MapTo.Tests.Infrastructure; -using Shouldly; -using Xunit; -using static MapTo.Tests.Common; - -namespace MapTo.Tests -{ - public class IgnorePropertyAttributeTests - { - /* - [Fact] - public void VerifyIgnorePropertyAttribute() - { - // Arrange - const string source = ""; - var expectedAttribute = $@" -{Constants.GeneratedFilesHeader} -using System; - -namespace MapTo -{{ - [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)] - public sealed class IgnorePropertyAttribute : Attribute {{ }} -}} -".Trim(); - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.ShouldContainSource(IgnorePropertyAttributeSource.AttributeName, expectedAttribute); - } - */ - - [Fact] - public void When_IgnorePropertyAttributeIsSpecified_Should_NotGenerateMappingsForThatProperty() - { - // Arrange - var source = GetSourceText(new SourceGeneratorOptions( - true, - PropertyBuilder: builder => - { - builder - .WriteLine("[IgnoreProperty]") - .WriteLine("public int Prop4 { get; set; }"); - }, - SourcePropertyBuilder: builder => builder.WriteLine("public int Prop4 { get; set; }"))); - - var expectedResult = @" - partial class Foo - { - public Foo(Test.Models.Baz baz) - : this(new MappingContext(), baz) { } - - private protected Foo(MappingContext context, Test.Models.Baz baz) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - if (baz == null) throw new ArgumentNullException(nameof(baz)); - - context.Register(baz, this); - - Prop1 = baz.Prop1; - Prop2 = baz.Prop2; - Prop3 = baz.Prop3; - } -".Trim(); - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.Last().ShouldContainPartialSource(expectedResult); - } - } -} \ No newline at end of file diff --git a/test/BlueWest.MapTo.Tests/Infrastructure/CSharpGenerator.cs b/test/BlueWest.MapTo.Tests/Infrastructure/CSharpGenerator.cs deleted file mode 100644 index f08b307..0000000 --- a/test/BlueWest.MapTo.Tests/Infrastructure/CSharpGenerator.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using MapTo.Tests.Extensions; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; - -namespace MapTo.Tests.Infrastructure -{ - internal static class CSharpGenerator - { - internal static (Compilation compilation, ImmutableArray diagnostics) GetOutputCompilation( - string source, - bool assertCompilation = false, - IDictionary? analyzerConfigOptions = null, - NullableContextOptions nullableContextOptions = NullableContextOptions.Disable, - LanguageVersion languageVersion = LanguageVersion.CSharp7_3) => - GetOutputCompilation( - new[] { source }, - assertCompilation, - analyzerConfigOptions, - nullableContextOptions, - languageVersion); - - internal static (Compilation compilation, ImmutableArray diagnostics) GetOutputCompilation( - IEnumerable sources, - bool assertCompilation = false, - IDictionary? analyzerConfigOptions = null, - NullableContextOptions nullableContextOptions = NullableContextOptions.Disable, - LanguageVersion languageVersion = LanguageVersion.CSharp7_3) - { - var references = AppDomain.CurrentDomain.GetAssemblies() - .Where(a => !a.IsDynamic && !string.IsNullOrWhiteSpace(a.Location)) - .Select(a => MetadataReference.CreateFromFile(a.Location)) - .ToList(); - - var compilation = CSharpCompilation.Create( - $"{typeof(CSharpGenerator).Assembly.GetName().Name}.Dynamic", - sources.Select((source, index) => CSharpSyntaxTree.ParseText(source, path: $"Test{index:00}.g.cs", options: new CSharpParseOptions(languageVersion))), - references, - new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, nullableContextOptions: nullableContextOptions)); - - if (assertCompilation) - { - // NB: fail tests when the injected program isn't valid _before_ running generators - compilation.GetDiagnostics().ShouldBeSuccessful(); - } - - var driver = CSharpGeneratorDriver.Create( - new List() { new MapToGenerator(), new EfMethodsGenerator() }, - optionsProvider: new TestAnalyzerConfigOptionsProvider(analyzerConfigOptions), - parseOptions: new CSharpParseOptions(languageVersion) - ); - - driver.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out var generateDiagnostics); - - generateDiagnostics.ShouldBeSuccessful(ignoreDiagnosticsIds: new[] { "MT" }); - outputCompilation.GetDiagnostics().ShouldBeSuccessful(outputCompilation); - - return (outputCompilation, generateDiagnostics); - } - } -} \ No newline at end of file diff --git a/test/BlueWest.MapTo.Tests/Infrastructure/TestAnalyzerConfigOptions.cs b/test/BlueWest.MapTo.Tests/Infrastructure/TestAnalyzerConfigOptions.cs deleted file mode 100644 index 462d71d..0000000 --- a/test/BlueWest.MapTo.Tests/Infrastructure/TestAnalyzerConfigOptions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace MapTo.Tests.Infrastructure -{ - internal sealed class TestAnalyzerConfigOptions : AnalyzerConfigOptions - { - private readonly ImmutableDictionary _backing; - - public TestAnalyzerConfigOptions(IDictionary? properties) - { - _backing = properties?.ToImmutableDictionary(KeyComparer) ?? ImmutableDictionary.Create(KeyComparer); - } - - public override bool TryGetValue(string key, out string? value) => _backing.TryGetValue(key, out value); - } -} \ No newline at end of file diff --git a/test/BlueWest.MapTo.Tests/Infrastructure/TestAnalyzerConfigOptionsProvider.cs b/test/BlueWest.MapTo.Tests/Infrastructure/TestAnalyzerConfigOptionsProvider.cs deleted file mode 100644 index 914901d..0000000 --- a/test/BlueWest.MapTo.Tests/Infrastructure/TestAnalyzerConfigOptionsProvider.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace MapTo.Tests.Infrastructure -{ - internal sealed class TestAnalyzerConfigOptionsProvider : AnalyzerConfigOptionsProvider - { - public TestAnalyzerConfigOptionsProvider(IDictionary? options) - { - GlobalOptions = new TestAnalyzerConfigOptions(options); - } - - /// - public override AnalyzerConfigOptions GlobalOptions { get; } - - /// - public override AnalyzerConfigOptions GetOptions(SyntaxTree tree) => throw new NotImplementedException(); - - /// - public override AnalyzerConfigOptions GetOptions(AdditionalText textFile) => throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/test/BlueWest.MapTo.Tests/MapPropertyTests.cs b/test/BlueWest.MapTo.Tests/MapPropertyTests.cs deleted file mode 100644 index 54af284..0000000 --- a/test/BlueWest.MapTo.Tests/MapPropertyTests.cs +++ /dev/null @@ -1,202 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using MapTo.Sources; -using MapTo.Tests.Extensions; -using MapTo.Tests.Infrastructure; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Xunit; -using static MapTo.Tests.Common; - -namespace MapTo.Tests -{ - public class MapPropertyTests - { - [Theory] - [InlineData(NullableContextOptions.Disable)] - [InlineData(NullableContextOptions.Enable)] - public void VerifyMapPropertyAttribute(NullableContextOptions nullableContextOptions) - { - // Arrange - const string source = ""; - var nullableSyntax = nullableContextOptions == NullableContextOptions.Enable ? "?" : string.Empty; - var languageVersion = nullableContextOptions == NullableContextOptions.Enable ? LanguageVersion.CSharp8 : LanguageVersion.CSharp7_3; - var expectedInterface = $@" -{Constants.GeneratedFilesHeader} -{(nullableContextOptions == NullableContextOptions.Enable ? $"#nullable enable{Environment.NewLine}" : string.Empty)} -using System; - -namespace MapTo -{{ - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = true)] - public sealed class MapPropertyAttribute : Attribute - {{ - public string{nullableSyntax} SourcePropertyName {{ get; set; }} - }} -}} -".Trim(); - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions, nullableContextOptions: nullableContextOptions, languageVersion: languageVersion); - - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.ShouldContainSource(MapPropertyAttributeSource.AttributeName, expectedInterface); - } - - [Fact] - public void When_MapPropertyFound_Should_UseItToMapToSourceProperty() - { - // Arrange - var source = GetSourceText(new SourceGeneratorOptions( - true, - PropertyBuilder: builder => - { - builder - .WriteLine("[MapProperty(SourcePropertyName = nameof(Baz.Prop3))]") - .WriteLine("public int Prop4 { get; set; }"); - }, - SourcePropertyBuilder: builder => builder.WriteLine("public int Prop4 { get; set; }"))); - - var expectedResult = @" - partial class Foo - { - public Foo(Test.Models.Baz baz) - : this(new MappingContext(), baz) { } - - private protected Foo(MappingContext context, Test.Models.Baz baz) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - if (baz == null) throw new ArgumentNullException(nameof(baz)); - - context.Register(baz, this); - - Prop1 = baz.Prop1; - Prop2 = baz.Prop2; - Prop3 = baz.Prop3; - Prop4 = baz.Prop3; - } -".Trim(); - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.Last().ShouldContainPartialSource(expectedResult); - } - - [Theory] - [MemberData(nameof(MapPropertyWithImplicitConversionFoundData))] - public void When_MapPropertyWithImplicitConversionFound_Should_UseItToMapToSourceProperty(string source, string expectedResult, LanguageVersion languageVersion) - { - // Arrange - source = source.Trim(); - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions, languageVersion: languageVersion); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.Last().ShouldContainPartialSource(expectedResult); - } - - public static IEnumerable MapPropertyWithImplicitConversionFoundData => new List - { - new object[] - { - @" -namespace Test -{ - using System.Collections.Generic; - - public class InnerClass { public int Prop1 { get; set; } } - public class OuterClass - { - public int Id { get; set; } - public List InnerProp { get; set; } - } -} - -namespace Test.Models -{ - using MapTo; - using System.Collections.Generic; - - [MapFrom(typeof(Test.InnerClass))] - public partial class InnerClass { public int Prop1 { get; set; } } - - [MapFrom(typeof(Test.OuterClass))] - public partial class OuterClass - { - public int Id { get; set; } - public IReadOnlyList InnerProp { get; set; } - } -} -", - @" - private protected OuterClass(MappingContext context, Test.OuterClass outerClass) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - if (outerClass == null) throw new ArgumentNullException(nameof(outerClass)); - - context.Register(outerClass, this); - - Id = outerClass.Id; - InnerProp = outerClass.InnerProp.Select(context.MapFromWithContext).ToList(); - } -", - LanguageVersion.CSharp7_3 - }, - new object[] - { - @" -namespace Test -{ - using System; - using System.Collections.Generic; - - public class InnerClass - { - public int Id { get; set; } - public string Name { get; set; } - } - - public class OuterClass - { - public int Id { get; set; } - public List InnerClasses { get; set; } - public DateTime? SomeDate { get; set; } - } -} - -namespace Test.Models -{ - using MapTo; - using System; - using System.Collections.Generic; - - [MapFrom(typeof(Test.InnerClass))] - public partial record InnerClass(int Id, string Name); - - [MapFrom(typeof(Test.OuterClass))] - public partial record OuterClass(int Id, DateTime? SomeDate, IReadOnlyList InnerClasses); -} -", - @" - private protected OuterClass(MappingContext context, Test.OuterClass outerClass) - : this(Id: outerClass.Id, SomeDate: outerClass.SomeDate, InnerClasses: outerClass.InnerClasses.Select(context.MapFromWithContext).ToList()) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - if (outerClass == null) throw new ArgumentNullException(nameof(outerClass)); - - context.Register(outerClass, this); - } -", - LanguageVersion.CSharp9 - } - }; - } -} \ No newline at end of file diff --git a/test/BlueWest.MapTo.Tests/MapToTests.cs b/test/BlueWest.MapTo.Tests/MapToTests.cs deleted file mode 100644 index 2e133a0..0000000 --- a/test/BlueWest.MapTo.Tests/MapToTests.cs +++ /dev/null @@ -1,601 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using MapTo.Sources; -using MapTo.Tests.Extensions; -using MapTo.Tests.Infrastructure; -using Shouldly; -using Xunit; -using static MapTo.Extensions.GeneratorExecutionContextExtensions; -using static MapTo.Tests.Common; - -namespace MapTo.Tests -{ - public class MapToTests - { - private static readonly string ExpectedAttribute = $@"{Constants.GeneratedFilesHeader} -using System; - -namespace MapTo -{{ - [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] - public sealed class MapFromAttribute : Attribute - {{ - public MapFromAttribute(Type sourceType) - {{ - SourceType = sourceType; - }} - - public Type SourceType {{ get; }} - }} -}}"; - - [Fact] - public void VerifyMapToAttribute() - { - // Arrange - const string source = ""; - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.ShouldContainSource(MapFromAttributeSource.AttributeClassName, ExpectedAttribute); - } - - [Fact] - public void When_FoundMatchingPropertyNameWithDifferentTypes_Should_ReportError() - { - // Arrange - var source = GetSourceText(new SourceGeneratorOptions( - true, - PropertyBuilder: builder => { builder.WriteLine("public string Prop4 { get; set; }"); }, - SourcePropertyBuilder: builder => builder.WriteLine("public int Prop4 { get; set; }"))); - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - var expectedError = DiagnosticsFactory.NoMatchingPropertyTypeFoundError(GetSourcePropertySymbol("Prop4", compilation)); - - diagnostics.ShouldNotBeSuccessful(expectedError); - } - - [Fact] - public void When_MappingsModifierOptionIsSetToInternal_Should_GenerateThoseMethodsWithInternalAccessModifier() - { - // Arrange - var source = GetSourceText(); - var configOptions = new Dictionary - { - [GetBuildPropertyName(nameof(SourceGenerationOptions.GeneratedMethodsAccessModifier))] = "Internal", - [GetBuildPropertyName(nameof(SourceGenerationOptions.GenerateXmlDocument))] = "false" - }; - - var expectedExtension = @" - internal static partial class BazToFooExtensions - { - internal static Foo ToFoo(this Test.Models.Baz baz) - { - return baz == null ? null : new Foo(baz); - } - }".Trim(); - - var expectedFactory = @" - internal static Foo From(Test.Models.Baz baz) - { - return baz == null ? null : MappingContext.Create(baz); - }".Trim(); - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: configOptions); - - // Assert - diagnostics.ShouldBeSuccessful(); - - var syntaxTree = compilation.SyntaxTrees.Last().ToString(); - syntaxTree.ShouldContain(expectedFactory); - syntaxTree.ShouldContain(expectedExtension); - } - - [Fact] - public void When_MapToAttributeFound_Should_GenerateTheClass() - { - // Arrange - const string source = @" -using MapTo; - -namespace Test -{ - [MapFrom(typeof(Baz))] - public partial class Foo - { - public int Prop1 { get; set; } - } - - public class Baz - { - public int Prop1 { get; set; } - } -} -"; - - const string expectedResult = @" -// -using MapTo; -using System; - -namespace Test -{ - partial class Foo - { - public Foo(Test.Baz baz) - : this(new MappingContext(), baz) { } - - private protected Foo(MappingContext context, Test.Baz baz) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - if (baz == null) throw new ArgumentNullException(nameof(baz)); - - context.Register(baz, this); - - Prop1 = baz.Prop1; - } -"; - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.Last().ToString().ShouldStartWith(expectedResult.Trim()); - } - - [Fact] - public void When_MapToAttributeFoundWithoutMatchingProperties_Should_ReportError() - { - // Arrange - const string source = @" -using MapTo; - -namespace Test -{ - [MapFrom(typeof(Baz))] - public partial class Foo { } - - public class Baz { public int Prop1 { get; set; } } -} -"; - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source); - - // Assert - var fooType = compilation.GetTypeByMetadataName("Test.Foo"); - fooType.ShouldNotBeNull(); - - var bazType = compilation.GetTypeByMetadataName("Test.Baz"); - bazType.ShouldNotBeNull(); - - var expectedDiagnostic = DiagnosticsFactory.NoMatchingPropertyFoundError(fooType.Locations.Single(), fooType, bazType); - var error = diagnostics.FirstOrDefault(d => d.Id == expectedDiagnostic.Id); - error.ShouldNotBeNull(); - } - - [Fact] - public void When_MapToAttributeWithNamespaceFound_Should_GenerateTheClass() - { - // Arrange - const string source = @" -namespace Test -{ - [MapTo.MapFrom(typeof(Baz))] - public partial class Foo { public int Prop1 { get; set; } } - - public class Baz { public int Prop1 { get; set; } } -} -"; - - const string expectedResult = @" -// -using MapTo; -using System; - -namespace Test -{ - partial class Foo - { - public Foo(Test.Baz baz) - : this(new MappingContext(), baz) { } - - private protected Foo(MappingContext context, Test.Baz baz) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - if (baz == null) throw new ArgumentNullException(nameof(baz)); - - context.Register(baz, this); - - Prop1 = baz.Prop1; - } -"; - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.Last().ToString().ShouldStartWith(expectedResult.Trim()); - } - - [Fact] - public void When_NoMapToAttributeFound_Should_GenerateOnlyTheAttribute() - { - // Arrange - const string source = ""; - var expectedTypes = new[] - { - //IgnorePropertyAttributeSource.AttributeName, - MapFromAttributeSource.AttributeName, - ITypeConverterSource.InterfaceName, - MapPropertyAttributeSource.AttributeName - }; - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees - .Select(s => s.ToString()) - .Where(s => !string.IsNullOrWhiteSpace(s.ToString())) - .All(s => expectedTypes.Any(s.Contains)) - .ShouldBeTrue(); - } - - [Fact] - public void When_SourceTypeHasDifferentNamespace_Should_NotAddToUsings() - { - // Arrange - var source = GetSourceText(new SourceGeneratorOptions(SourceClassNamespace: "Bazaar")); - - const string expectedResult = @" -// -using MapTo; -using System; - -namespace Test -{ -"; - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.Last().ToString().ShouldStartWith(expectedResult.Trim()); - } - - [Fact] - public void When_SourceTypeHasMatchingProperties_Should_CreateConstructorAndAssignSrcToDest() - { - // Arrange - var source = GetSourceText(); - - const string expectedResult = @" - partial class Foo - { - public Foo(Test.Models.Baz baz) - : this(new MappingContext(), baz) { } - - private protected Foo(MappingContext context, Test.Models.Baz baz) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - if (baz == null) throw new ArgumentNullException(nameof(baz)); - - context.Register(baz, this); - - Prop1 = baz.Prop1; - Prop2 = baz.Prop2; - Prop3 = baz.Prop3; - } -"; - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.Last().ShouldContainPartialSource(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 : MappingContext.Create(baz); - } -"; - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.Last().ShouldContainPartialSource(expectedResult.Trim()); - } - - [Fact] - public void When_SourceTypeHasMatchingProperties_Should_GenerateToExtensionMethodOnSourceType() - { - // Arrange - var source = GetSourceText(); - - const string expectedResult = @" - public static partial class BazToFooExtensions - { - public static Foo ToFoo(this Test.Models.Baz baz) - { - return baz == null ? null : new Foo(baz); - } - } -"; - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.Last().ShouldContainPartialSource(expectedResult.Trim()); - } - - [Fact] - public void When_HasNestedObjectPropertyTypeHasMapFromAttribute_Should_UseContinueToMap() - { - // Arrange - var source = GetSourceText(new SourceGeneratorOptions( - SourceClassNamespace: "Test", - PropertyBuilder: b => b.WriteLine("public B InnerProp1 { get; }"), - SourcePropertyBuilder: b => b.WriteLine("public A InnerProp1 { get; }"))); - - source += @" -namespace Test -{ - public class A { public int Prop1 { get; } } - - [MapTo.MapFrom(typeof(A))] - public partial class B { public int Prop1 { get; }} -} -".Trim(); - - var expectedResult = @" - partial class Foo - { - public Foo(Test.Baz baz) - : this(new MappingContext(), baz) { } - - private protected Foo(MappingContext context, Test.Baz baz) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - if (baz == null) throw new ArgumentNullException(nameof(baz)); - - context.Register(baz, this); - - Prop1 = baz.Prop1; - Prop2 = baz.Prop2; - Prop3 = baz.Prop3; - InnerProp1 = context.MapFromWithContext(baz.InnerProp1); - } -".Trim(); - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.ToArray()[^2].ShouldContainPartialSource(expectedResult); - } - - [Fact] - public void When_HasNestedObjectPropertyTypeDoesNotHaveMapFromAttribute_Should_ReportError() - { - // Arrange - var source = GetSourceText(new SourceGeneratorOptions( - SourceClassNamespace: "Test", - PropertyBuilder: b => b.WriteLine("public FooInner1 InnerProp1 { get; }"), - SourcePropertyBuilder: b => b.WriteLine("public BazInner1 InnerProp1 { get; }"))); - - source += @" -namespace Test -{ - public class FooInner1 { public int Prop1 { get; } } - - public partial class BazInner1 { public int Prop1 { get; }} -} -".Trim(); - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - var expectedError = DiagnosticsFactory.NoMatchingPropertyTypeFoundError(GetSourcePropertySymbol("InnerProp1", compilation)); - diagnostics.ShouldNotBeSuccessful(expectedError); - } - - [Fact] - public void When_HasNestedObjectPropertyTypeHasMapFromAttributeToDifferentType_Should_ReportError() - { - // Arrange - var source = GetSourceText(new SourceGeneratorOptions( - SourceClassNamespace: "Test", - PropertyBuilder: b => b.WriteLine("public FooInner1 InnerProp1 { get; }"), - SourcePropertyBuilder: b => b.WriteLine("public BazInner1 InnerProp1 { get; }"))); - - source += @" -namespace Test -{ - public class FooInner1 { public int Prop1 { get; } } - - public class FooInner2 { public int Prop1 { get; } } - - [MapTo.MapFrom(typeof(FooInner2))] - public partial class BazInner1 { public int Prop1 { get; }} -} -".Trim(); - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - var expectedError = DiagnosticsFactory.NoMatchingPropertyTypeFoundError(GetSourcePropertySymbol("InnerProp1", compilation)); - diagnostics.ShouldNotBeSuccessful(expectedError); - } - - [Fact] - public void When_SourceTypeEnumerableProperties_Should_CreateConstructorAndAssignSrcToDest() - { - // Arrange - var source = GetSourceText(new SourceGeneratorOptions( - Usings: new[] { "System.Collections.Generic"}, - PropertyBuilder: builder => builder.WriteLine("public IEnumerable Prop4 { get; }"), - SourcePropertyBuilder: builder => builder.WriteLine("public IEnumerable Prop4 { get; }"))); - - const string expectedResult = @" - partial class Foo - { - public Foo(Test.Models.Baz baz) - : this(new MappingContext(), baz) { } - - private protected Foo(MappingContext context, Test.Models.Baz baz) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - if (baz == null) throw new ArgumentNullException(nameof(baz)); - - context.Register(baz, this); - - Prop1 = baz.Prop1; - Prop2 = baz.Prop2; - Prop3 = baz.Prop3; - Prop4 = baz.Prop4; - } -"; - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.Last().ShouldContainPartialSource(expectedResult.Trim()); - } - - [Fact] - public void When_DestinationTypeHasBaseClass_Should_CallBaseConstructor() - { - // Arrange - var sources = GetEmployeeManagerSourceText(); - - const string expectedResult = @" -private protected ManagerViewModel(MappingContext context, Test.Data.Models.Manager manager) : base(context, manager) -{ - if (context == null) throw new ArgumentNullException(nameof(context)); - if (manager == null) throw new ArgumentNullException(nameof(manager)); - - context.Register(manager, this); - - Level = manager.Level; - Employees = manager.Employees.Select(context.MapFromWithContext).ToList(); -} -"; - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(sources, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.Last().ShouldContainPartialSource(expectedResult); - } - - [Fact] - public void When_SourceTypeHasEnumerablePropertiesWithMapFromAttribute_Should_CreateANewEnumerableWithMappedObjects() - { - // Arrange - var sources = GetEmployeeManagerSourceText(); - - const string expectedResult = @" -// -using MapTo; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Test.ViewModels -{ - partial class ManagerViewModel - { - public ManagerViewModel(Test.Data.Models.Manager manager) - : this(new MappingContext(), manager) { } - - private protected ManagerViewModel(MappingContext context, Test.Data.Models.Manager manager) : base(context, manager) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - if (manager == null) throw new ArgumentNullException(nameof(manager)); - - context.Register(manager, this); - - Level = manager.Level; - Employees = manager.Employees.Select(context.MapFromWithContext).ToList(); - } -"; - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(sources, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.Last().ShouldContainPartialSource(expectedResult); - } - - [Fact] - public void When_SourceTypeHasEnumerablePropertiesWithMapFromAttributeInDifferentNamespaces_Should_CreateANewEnumerableWithMappedObjectsAndImportNamespace() - { - // Arrange - var sources = GetEmployeeManagerSourceText(useDifferentViewModelNamespace: true); - - const string expectedResult = @" -using MapTo; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Test.ViewModels2 -{ - partial class ManagerViewModel - { - public ManagerViewModel(Test.Data.Models.Manager manager) - : this(new MappingContext(), manager) { } - - private protected ManagerViewModel(MappingContext context, Test.Data.Models.Manager manager) : base(context, manager) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - if (manager == null) throw new ArgumentNullException(nameof(manager)); - - context.Register(manager, this); - - Level = manager.Level; - Employees = manager.Employees.Select(context.MapFromWithContext).ToList(); - } -"; - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(sources, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.Last().ShouldContainPartialSource(expectedResult); - } - } -} \ No newline at end of file diff --git a/test/BlueWest.MapTo.Tests/MapTypeConverterTests.cs b/test/BlueWest.MapTo.Tests/MapTypeConverterTests.cs deleted file mode 100644 index abe0511..0000000 --- a/test/BlueWest.MapTo.Tests/MapTypeConverterTests.cs +++ /dev/null @@ -1,283 +0,0 @@ -using System.Linq; -using MapTo.Sources; -using MapTo.Tests.Extensions; -using MapTo.Tests.Infrastructure; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Xunit; -using static MapTo.Tests.Common; - -namespace MapTo.Tests -{ - public class MapTypeConverterTests - { - [Fact] - public void VerifyMapTypeConverterAttribute() - { - // Arrange - const string source = ""; - var expectedInterface = $@" -{Constants.GeneratedFilesHeader} - -using System; - -namespace MapTo -{{ - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false)] - public sealed class MapTypeConverterAttribute : Attribute - {{ - public MapTypeConverterAttribute(Type converter, object[] converterParameters = null) - {{ - Converter = converter; - ConverterParameters = converterParameters; - }} - - public Type Converter {{ get; }} - - public object[] ConverterParameters {{ get; }} - }} -}} -".Trim(); - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.ShouldContainSource(MapTypeConverterAttributeSource.AttributeName, expectedInterface); - } - - [Fact] - public void VerifyMapTypeConverterAttributeWithNullableOptionOn() - { - // Arrange - const string source = ""; - var expectedInterface = $@" -{Constants.GeneratedFilesHeader} -#nullable enable - -using System; - -namespace MapTo -{{ - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false)] - public sealed class MapTypeConverterAttribute : Attribute - {{ - public MapTypeConverterAttribute(Type converter, object[]? converterParameters = null) - {{ - Converter = converter; - ConverterParameters = converterParameters; - }} - - public Type Converter {{ get; }} - - public object[]? ConverterParameters {{ get; }} - }} -}} -".Trim(); - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions, nullableContextOptions: NullableContextOptions.Enable, languageVersion: LanguageVersion.CSharp8); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.ShouldContainSource(MapTypeConverterAttributeSource.AttributeName, expectedInterface); - } - - [Fact] - public void VerifyTypeConverterInterface() - { - // Arrange - const string source = ""; - var expectedInterface = $@" -{Constants.GeneratedFilesHeader} - -namespace MapTo -{{ - public interface ITypeConverter - {{ - TDestination Convert(TSource source, object[] converterParameters); - }} -}} -".Trim(); - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.ShouldContainSource(ITypeConverterSource.InterfaceName, expectedInterface); - } - - [Fact] - public void VerifyTypeConverterInterfaceWithNullableOptionOn() - { - // Arrange - const string source = ""; - var expectedInterface = $@" -{Constants.GeneratedFilesHeader} -#nullable enable - -namespace MapTo -{{ - public interface ITypeConverter - {{ - TDestination Convert(TSource source, object[]? converterParameters); - }} -}} -".Trim(); - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions, nullableContextOptions: NullableContextOptions.Enable, languageVersion: LanguageVersion.CSharp8); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.ShouldContainSource(ITypeConverterSource.InterfaceName, expectedInterface); - } - - [Fact] - public void When_FoundMatchingPropertyNameWithConverterType_ShouldUseTheConverterAndItsParametersToAssignProperties() - { - // Arrange - var source = GetSourceText(new SourceGeneratorOptions( - true, - PropertyBuilder: builder => - { - builder - .WriteLine("[MapTypeConverter(typeof(Prop4Converter), new object[]{\"G\", 'C', 10})]") - .WriteLine("public string Prop4 { get; set; }"); - }, - SourcePropertyBuilder: builder => builder.WriteLine("public long Prop4 { get; set; }"))); - - source += @" -namespace Test -{ - using MapTo; - - public class Prop4Converter: ITypeConverter - { - 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 - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.Last().ShouldContainPartialSource(expectedSyntax); - } - - [Fact] - public void When_FoundMatchingPropertyNameWithConverterType_ShouldUseTheConverterToAssignProperties() - { - // Arrange - var source = GetSourceText(new SourceGeneratorOptions( - true, - PropertyBuilder: builder => - { - builder - .WriteLine("[MapTypeConverter(typeof(Prop4Converter))]") - .WriteLine("public long Prop4 { get; set; }"); - }, - SourcePropertyBuilder: builder => builder.WriteLine("public string Prop4 { get; set; }"))); - - source += @" -namespace Test -{ - using MapTo; - - public class Prop4Converter: ITypeConverter - { - public long Convert(string source, object[] converterParameters) => long.Parse(source); - } -} -"; - - const string expectedSyntax = "Prop4 = new Test.Prop4Converter().Convert(baz.Prop4, null);"; - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.Last().ShouldContainPartialSource(expectedSyntax); - } - - [Fact] - public void When_FoundMatchingPropertyNameWithDifferentImplicitlyConvertibleType_Should_GenerateTheProperty() - { - // Arrange - var source = GetSourceText(new SourceGeneratorOptions( - true, - PropertyBuilder: builder => { builder.WriteLine("public long Prop4 { get; set; }"); }, - SourcePropertyBuilder: builder => builder.WriteLine("public int Prop4 { get; set; }"))); - - var expectedResult = @" - partial class Foo - { - public Foo(Test.Models.Baz baz) - : this(new MappingContext(), baz) { } - - private protected Foo(MappingContext context, Test.Models.Baz baz) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - if (baz == null) throw new ArgumentNullException(nameof(baz)); - - context.Register(baz, this); - - Prop1 = baz.Prop1; - Prop2 = baz.Prop2; - Prop3 = baz.Prop3; - Prop4 = baz.Prop4; - } -".Trim(); - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.Last().ShouldContainPartialSource(expectedResult); - } - - [Fact] - public void When_FoundMatchingPropertyNameWithIncorrectConverterType_ShouldReportError() - { - // Arrange - var source = GetSourceText(new SourceGeneratorOptions( - true, - PropertyBuilder: builder => - { - builder - .WriteLine("[IgnoreProperty]") - .WriteLine("public long IgnoreMe { get; set; }") - .WriteLine("[MapTypeConverter(typeof(Prop4Converter))]") - .WriteLine("public long Prop4 { get; set; }"); - }, - SourcePropertyBuilder: builder => builder.WriteLine("public string Prop4 { get; set; }"))); - - source += @" -namespace Test -{ - using MapTo; - - public class Prop4Converter: ITypeConverter - { - public int Convert(string source, object[] converterParameters) => int.Parse(source); - } -} -"; - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - var expectedError = DiagnosticsFactory.InvalidTypeConverterGenericTypesError(GetSourcePropertySymbol("Prop4", compilation), GetSourcePropertySymbol("Prop4", compilation, "Baz")); - diagnostics.ShouldNotBeSuccessful(expectedError); - } - } -} \ No newline at end of file diff --git a/test/BlueWest.MapTo.Tests/MappedClassesTests.cs b/test/BlueWest.MapTo.Tests/MappedClassesTests.cs deleted file mode 100644 index 517e04e..0000000 --- a/test/BlueWest.MapTo.Tests/MappedClassesTests.cs +++ /dev/null @@ -1,562 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using MapTo.Tests.Extensions; -using MapTo.Tests.Infrastructure; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Shouldly; -using Xunit; -using Xunit.Abstractions; -using static MapTo.Tests.Common; - -namespace MapTo.Tests -{ - public class MappedClassesTests - { - private readonly ITestOutputHelper _output; - - public MappedClassesTests(ITestOutputHelper output) - { - _output = output; - } - - [Theory] - [MemberData(nameof(SecondaryConstructorCheckData))] - public void When_SecondaryConstructorExists_Should_NotGenerateOne(string source, LanguageVersion languageVersion) - { - // Arrange - source = source.Trim(); - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions, languageVersion: languageVersion); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation - .GetGeneratedSyntaxTree("DestinationClass") - .ShouldNotBeNull() - .GetRoot() - .DescendantNodes() - .OfType() - .Count() - .ShouldBe(1); - } - - public static IEnumerable SecondaryConstructorCheckData => new List - { - new object[] - { - @" -using MapTo; -namespace Test.Data.Models -{ - public class SourceClass { public string Prop1 { get; set; } } - - [MapFrom(typeof(SourceClass))] - public partial class DestinationClass - { - public DestinationClass(SourceClass source) : this(new MappingContext(), source) { } - public string Prop1 { get; set; } - } -} -", - LanguageVersion.CSharp7_3 - }, - new object[] - { - @" -using MapTo; -namespace Test.Data.Models -{ - public record SourceClass(string Prop1); - - [MapFrom(typeof(SourceClass))] - public partial record DestinationClass(string Prop1) - { - public DestinationClass(SourceClass source) : this(new MappingContext(), source) { } - } -} -", - LanguageVersion.CSharp9 - } - }; - - [Theory] - [MemberData(nameof(SecondaryCtorWithoutPrivateCtorData))] - public void When_SecondaryConstructorExistsButDoNotReferencePrivateConstructor_Should_ReportError(string source, LanguageVersion languageVersion) - { - // Arrange - source = source.Trim(); - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions, languageVersion: languageVersion); - - // Assert - var constructorSyntax = compilation.SyntaxTrees - .First() - .GetRoot() - .DescendantNodes() - .OfType() - .Single(); - - diagnostics.ShouldNotBeSuccessful(DiagnosticsFactory.MissingConstructorArgument(constructorSyntax)); - } - - public static IEnumerable SecondaryCtorWithoutPrivateCtorData => new List - { - new object[] - { - @" -using MapTo; -namespace Test.Data.Models -{ - public class SourceClass { public string Prop1 { get; set; } } - - [MapFrom(typeof(SourceClass))] - public partial class DestinationClass - { - public DestinationClass(SourceClass source) { } - public string Prop1 { get; set; } - } -} -", - LanguageVersion.CSharp7_3 - }, - new object[] - { - @" -using MapTo; -namespace Test.Data.Models -{ - public record SourceClass(string Prop1); - - [MapFrom(typeof(SourceClass))] - public partial record DestinationClass(string Prop1) - { - public DestinationClass(SourceClass source) : this(""invalid"") { } - } -} -", - LanguageVersion.CSharp9 - } - }; - - [Fact] - public void When_PropertyNameIsTheSameAsClassName_Should_MapAccordingly() - { - // Arrange - var source = @" -namespace Sale -{ - public class Sale { public Sale Prop1 { get; set; } } -} - -namespace SaleModel -{ - using MapTo; - using Sale; - - [MapFrom(typeof(Sale))] - public partial class SaleModel - { - [MapProperty(SourcePropertyName = nameof(global::Sale.Sale.Prop1))] - public Sale Sale { get; set; } - } -} -".Trim(); - - // Act - var (_, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - diagnostics.ShouldBeSuccessful(); - } - - [Theory] - [MemberData(nameof(SameSourceAndDestinationTypeNameData))] - public void When_SourceAndDestinationNamesAreTheSame_Should_MapAccordingly(string source, LanguageVersion languageVersion) - { - // Arrange - source = source.Trim(); - - // Act - var (_, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions, languageVersion: languageVersion); - - // Assert - diagnostics.ShouldBeSuccessful(); - } - - public static IEnumerable SameSourceAndDestinationTypeNameData => new List - { - new object[] - { - @" -namespace Test -{ - public class TypeName { public int Prop2 { get; set; } } -} - -namespace Test2 -{ - using MapTo; - - [MapFrom(typeof(Test.TypeName))] - public partial class TypeName - { - [MapProperty(SourcePropertyName=""Prop2"")] - public int Prop1 { get; set; } - } -}", - LanguageVersion.CSharp7_3 - }, - new object[] - { - @" -namespace Test -{ - public record TypeName(int Prop2); -} - -namespace Test2 -{ - using MapTo; - - [MapFrom(typeof(Test.TypeName))] - public partial record TypeName([MapProperty(SourcePropertyName=""Prop2"")] int Prop1); -}", - LanguageVersion.CSharp9 - }, - new object[] - { - @" -namespace Test -{ - using System.Collections.Generic; - - public class SourceType2 { public int Id { get; set; } } - public class SourceType - { - public int Id { get; set; } - public List Prop1 { get; set; } - } -} - -namespace Test2 -{ - using MapTo; - using System.Collections.Generic; - - [MapFrom(typeof(Test.SourceType2))] - public partial class SourceType2 { public int Id { get; set; } } - - [MapFrom(typeof(Test.SourceType))] - public partial class SourceType - { - public int Id { get; set; } - public IReadOnlyList Prop1 { get; set; } - } -}", - LanguageVersion.CSharp7_3 - }, - new object[] - { - @" -namespace Test -{ - using System.Collections.Generic; - - public record SourceType(int Id, List Prop1); - public record SourceType2(int Id); -} - -namespace Test2 -{ - using MapTo; - using System.Collections.Generic; - - [MapFrom(typeof(Test.SourceType2))] - public partial record SourceType2(int Id); - - [MapFrom(typeof(Test.SourceType))] - public partial record SourceType(int Id, IReadOnlyList Prop1); -}", - LanguageVersion.CSharp9 - }, - new object[] - { - @" -namespace Test -{ - using System.Collections.Generic; - - public record SourceType1(int Id); - public record SourceType2(int Id, List Prop1); -} - -namespace Test -{ - using MapTo; - using System.Collections.Generic; - - [MapFrom(typeof(Test.SourceType1))] - public partial record SourceType3(int Id); - - [MapFrom(typeof(Test.SourceType2))] - public partial record SourceType4(int Id, IReadOnlyList Prop1); -}", - LanguageVersion.CSharp9 - } - }; - - [Theory] - [MemberData(nameof(VerifyMappedTypesData))] - public void VerifyMappedTypes(string[] sources, LanguageVersion languageVersion) - { - // Arrange - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(sources, analyzerConfigOptions: DefaultAnalyzerOptions, languageVersion: languageVersion); - - // Assert - diagnostics.ShouldBeSuccessful(); - _output.WriteLine(compilation.PrintSyntaxTree()); - } - - public static IEnumerable VerifyMappedTypesData => new List - { - new object[] { new[] { MainSourceClass, NestedSourceClass, MainDestinationClass, NestedDestinationClass }, LanguageVersion.CSharp7_3 }, - new object[] { new[] { MainSourceRecord, NestedSourceRecord, MainDestinationRecord, NestedDestinationRecord }, LanguageVersion.CSharp9 }, - new object[] - { - new[] - { - @" -namespace Test.Classes.Classes1 -{ - public class Class1 - { - public int Id { get; set; } - public string Name { get; set; } - } -}", - @" -using System; -using System.Collections.Generic; -using Test.Classes.Classes1; - -namespace Test.Classes.Classes2 -{ - public class Class2 - { - public int Id { get; set; } - public List Genres { get; set; } - public DateTime? ReleaseDate { get; set; } - } -}", - @" -using MapTo; -using System; -using System.Collections.Generic; -using TC = Test.Classes; - -namespace Tests.Records -{ - [MapFrom(typeof(Test.Classes.Classes1.Class1))] - public partial record Class1(int Id, string Name); - - [MapFrom(typeof(Test.Classes.Classes2.Class2))] - public partial record Class2(int Id, IReadOnlyList Genres); -}" - }, - LanguageVersion.CSharp9 - } - }; - - [Fact] - public void VerifySelfReferencingRecords() - { - // Arrange - var source = @" -namespace Tests.Data.Models -{ - using System.Collections.Generic; - - public record Employee(int Id, string EmployeeCode, Manager Manager); - - public record Manager(int Id, string EmployeeCode, Manager Manager, int Level, List Employees) : Employee(Id, EmployeeCode, Manager); -} - -namespace Tests.Data.ViewModels -{ - using System.Collections.Generic; - using Tests.Data.Models; - using MapTo; - - [MapFrom(typeof(Employee))] - public partial record EmployeeViewModel(int Id, string EmployeeCode, ManagerViewModel Manager); - - [MapFrom(typeof(Manager))] - public partial record ManagerViewModel(int Id, string EmployeeCode, ManagerViewModel Manager, int Level, List Employees) : EmployeeViewModel(Id, EmployeeCode, Manager); -} -".Trim(); - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions, languageVersion: LanguageVersion.CSharp9); - - // Assert - diagnostics.ShouldBeSuccessful(); - _output.WriteLine(compilation.PrintSyntaxTree()); - } - - [Fact] - public void VerifySystemNamespaceConflict() - { - // Arrange - var source = @" -namespace Test -{ - public record SomeRecord(int Id); -} - -namespace Test.Models -{ - using MapTo; - - [MapFrom(typeof(Test.SomeRecord))] - public partial record SomeRecordModel(int Id); -} - -namespace Test.System -{ - public interface IMyInterface { } -} -".Trim(); - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions, languageVersion: LanguageVersion.CSharp9); - - // Assert - diagnostics.ShouldBeSuccessful(); - _output.WriteLine(compilation.PrintSyntaxTree()); - } - - private static string MainSourceClass => @" -using System; - -namespace Test.Data.Models -{ - public class User - { - public int Id { get; set; } - - public DateTimeOffset RegisteredAt { get; set; } - - public Profile Profile { get; set; } - } -} -".Trim(); - - private static string NestedSourceClass => @" -namespace Test.Data.Models -{ - public class Profile - { - public string FirstName { get; set; } - - public string LastName { get; set; } - - public string FullName => $""{FirstName} {LastName}""; - } -} -".Trim(); - - private static string MainDestinationClass => @" -using System; -using MapTo; -using Test.Data.Models; - -namespace Test.ViewModels -{ - [MapFrom(typeof(User))] - public partial class UserViewModel - { - [MapProperty(SourcePropertyName = nameof(User.Id))] - [MapTypeConverter(typeof(IdConverter))] - public string Key { get; } - - public DateTimeOffset RegisteredAt { get; set; } - - // [IgnoreProperty] - public ProfileViewModel Profile { get; set; } - - private class IdConverter : ITypeConverter - { - public string Convert(int source, object[] converterParameters) => $""{source:X}""; - } - } -} -".Trim(); - - private static string NestedDestinationClass => @" -using MapTo; -using Test.Data.Models; - -namespace Test.ViewModels -{ - [MapFrom(typeof(Profile))] - public partial class ProfileViewModel - { - public string FirstName { get; } - - public string LastName { get; } - } -} -".Trim(); - - private static string MainSourceRecord => BuildSourceRecord("public record User(int Id, DateTimeOffset RegisteredAt, Profile Profile);"); - - private static string MainDestinationRecord => BuildDestinationRecord(@" -[MapFrom(typeof(User))] -public partial record UserViewModel( - [MapProperty(SourcePropertyName = nameof(User.Id))] - [MapTypeConverter(typeof(UserViewModel.IdConverter))] - string Key, - DateTimeOffset RegisteredAt, - Profile Profile) -{ - private class IdConverter : ITypeConverter - { - public string Convert(int source, object[] converterParameters) => $""{source:X}""; - } -}"); - - private static string NestedSourceRecord => BuildSourceRecord("public record Profile(string FirstName, string LastName) { public string FullName => $\"{FirstName} {LastName}\"; }"); - - private static string NestedDestinationRecord => BuildDestinationRecord("[MapFrom(typeof(Profile))] public partial record ProfileViewModel(string FirstName, string LastName);"); - - private static string BuildSourceRecord(string record) - { - return $@" -using System; - -namespace RecordTest.Data.Models -{{ - {record} -}} -".Trim(); - } - - private static string BuildDestinationRecord(string record) - { - return $@" -using System; -using MapTo; -using RecordTest.Data.Models; - -namespace RecordTest.ViewModels -{{ - {record} -}} -".Trim(); - } - } -} \ No newline at end of file diff --git a/test/BlueWest.MapTo.Tests/MappingContextTests.cs b/test/BlueWest.MapTo.Tests/MappingContextTests.cs deleted file mode 100644 index 410a0c5..0000000 --- a/test/BlueWest.MapTo.Tests/MappingContextTests.cs +++ /dev/null @@ -1,102 +0,0 @@ -using MapTo.Sources; -using MapTo.Tests.Extensions; -using MapTo.Tests.Infrastructure; -using Xunit; -using static MapTo.Tests.Common; - -namespace MapTo.Tests -{ - public class MappingContextTests - { - [Fact] - public void VerifyMappingContextSource() - { - // Arrange - const string source = ""; - var expected = @" -// - -using System; -using System.Collections.Generic; -using System.Reflection; - -namespace MapTo -{ - internal sealed class MappingContext - { - private readonly Dictionary _cache; - - internal MappingContext() - { - _cache = new Dictionary(1); - } - - internal static TMapped Create(TOriginal original) - { - if (original == null) throw new ArgumentNullException(nameof(original)); - - var context = new MappingContext(); - var mapped = context.MapFromWithContext(original); - - if (mapped == null) - { - throw new InvalidOperationException(); - } - - return mapped; - } - - internal TMapped MapFromWithContext(TOriginal original) - { - if (original == null) - { - return default(TMapped); - } - - if (!TryGetValue(original, out var mapped)) - { - var instance = Activator.CreateInstance(typeof(TMapped), BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { this, original }, null); - if (instance != null) - { - mapped = (TMapped)instance; - } - } - - return mapped; - } - - internal void Register(TOriginal original, TMapped mapped) - { - if (original == null) throw new ArgumentNullException(nameof(original)); - if (mapped == null) throw new ArgumentNullException(nameof(mapped)); - - if (!_cache.ContainsKey(original)) - { - _cache.Add(original, mapped); - } - } - - private bool TryGetValue(TOriginal original, out TMapped mapped) - { - if (original != null && _cache.TryGetValue(original, out var value)) - { - mapped = (TMapped)value; - return true; - } - - mapped = default(TMapped); - return false; - } - } -} -".Trim(); - - // Act - var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions); - - // Assert - diagnostics.ShouldBeSuccessful(); - compilation.SyntaxTrees.ShouldContainSource(MappingContextSource.ClassName, expected); - } - } -} \ No newline at end of file diff --git a/test/MapTo.Integration.Tests/CyclicReferenceTests.cs b/test/MapTo.Integration.Tests/CyclicReferenceTests.cs deleted file mode 100644 index 189d16f..0000000 --- a/test/MapTo.Integration.Tests/CyclicReferenceTests.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.Linq; -using MapTo.Integration.Tests.Data.Models; -using MapTo.Integration.Tests.Data.ViewModels; -using Shouldly; -using Xunit; - -namespace MapTo.Integration.Tests -{ - public class CyclicReferenceTests - { - [Fact] - public void VerifySelfReference() - { - // Arrange - var manager = new Manager { Id = 1, EmployeeCode = "M001", Level = 100 }; - manager.Manager = manager; - - // Act - var result = manager.ToManagerViewModel(); - - // Assert - result.Id.ShouldBe(manager.Id); - result.EmployeeCode.ShouldBe(manager.EmployeeCode); - result.Level.ShouldBe(manager.Level); - result.Manager.ShouldBeSameAs(result); - } - - [Fact] - public void VerifyNestedReference() - { - // Arrange - var manager1 = new Manager { Id = 100, EmployeeCode = "M001", Level = 100 }; - var manager2 = new Manager { Id = 102, EmployeeCode = "M002", Level = 100 }; - - var employee1 = new Employee { Id = 200, EmployeeCode = "E001"}; - var employee2 = new Employee { Id = 201, EmployeeCode = "E002"}; - - employee1.Manager = manager1; - employee2.Manager = manager2; - - manager2.Manager = manager1; - - // Act - var manager1ViewModel = manager1.ToManagerViewModel(); - - // Assert - manager1ViewModel.Id.ShouldBe(manager1.Id); - manager1ViewModel.Manager.ShouldBeNull(); - manager1ViewModel.Employees.Count.ShouldBe(2); - manager1ViewModel.Employees[0].Id.ShouldBe(employee1.Id); - manager1ViewModel.Employees[0].Manager.ShouldBeSameAs(manager1ViewModel); - manager1ViewModel.Employees[1].Id.ShouldBe(manager2.Id); - manager1ViewModel.Employees[1].Manager.ShouldBeSameAs(manager1ViewModel); - } - - [Fact] - public void VerifyNestedSelfReference() - { - // Arrange - var manager1 = new Manager { Id = 100, EmployeeCode = "M001", Level = 100 }; - var manager3 = new Manager { Id = 101, EmployeeCode = "M003", Level = 100 }; - var manager2 = new Manager { Id = 102, EmployeeCode = "M002", Level = 100 }; - - var employee1 = new Employee { Id = 200, EmployeeCode = "E001"}; - var employee2 = new Employee { Id = 201, EmployeeCode = "E002"}; - var employee3 = new Employee { Id = 202, EmployeeCode = "E003"}; - - employee1.Manager = manager1; - employee2.Manager = manager2; - employee3.Manager = manager3; - - manager2.Manager = manager1; - manager3.Manager = manager2; - - // Act - var manager3ViewModel = manager3.ToManagerViewModel(); - - // Assert - manager3ViewModel.Manager.ShouldNotBeNull(); - manager3ViewModel.Manager.Id.ShouldBe(manager2.Id); - manager3ViewModel.Manager.Manager.Id.ShouldBe(manager1.Id); - manager3ViewModel.Employees.All(e => ReferenceEquals(e.Manager, manager3ViewModel)).ShouldBeTrue(); - } - } -} \ No newline at end of file diff --git a/test/MapTo.Integration.Tests/Data/Models/Employee.cs b/test/MapTo.Integration.Tests/Data/Models/Employee.cs deleted file mode 100644 index 2944fc5..0000000 --- a/test/MapTo.Integration.Tests/Data/Models/Employee.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace MapTo.Integration.Tests.Data.Models -{ - public class Employee - { - private Manager _manager; - - public int Id { get; set; } - - public string EmployeeCode { get; set; } - - public Manager Manager - { - get => _manager; - set - { - if (value == null) - { - _manager.Employees.Remove(this); - } - else - { - value.Employees.Add(this); - } - - _manager = value; - } - } - } -} \ No newline at end of file diff --git a/test/MapTo.Integration.Tests/Data/Models/Manager.cs b/test/MapTo.Integration.Tests/Data/Models/Manager.cs deleted file mode 100644 index ffafe25..0000000 --- a/test/MapTo.Integration.Tests/Data/Models/Manager.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; - -namespace MapTo.Integration.Tests.Data.Models -{ - public class Manager : Employee - { - public int Level { get; set; } - - public List Employees { get; set; } = new(); - } -} \ No newline at end of file diff --git a/test/MapTo.Integration.Tests/Data/ViewModels/EmployeeViewModel.cs b/test/MapTo.Integration.Tests/Data/ViewModels/EmployeeViewModel.cs deleted file mode 100644 index 8ebaf88..0000000 --- a/test/MapTo.Integration.Tests/Data/ViewModels/EmployeeViewModel.cs +++ /dev/null @@ -1,14 +0,0 @@ -using MapTo.Integration.Tests.Data.Models; - -namespace MapTo.Integration.Tests.Data.ViewModels -{ - [MapFrom(typeof(Employee))] - public partial class EmployeeViewModel - { - public int Id { get; set; } - - public string EmployeeCode { get; set; } - - public ManagerViewModel Manager { get; set; } - } -} \ No newline at end of file diff --git a/test/MapTo.Integration.Tests/Data/ViewModels/ManagerViewModel.cs b/test/MapTo.Integration.Tests/Data/ViewModels/ManagerViewModel.cs deleted file mode 100644 index d085c24..0000000 --- a/test/MapTo.Integration.Tests/Data/ViewModels/ManagerViewModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; -using MapTo.Integration.Tests.Data.Models; - -namespace MapTo.Integration.Tests.Data.ViewModels -{ - [MapFrom(typeof(Manager))] - public partial class ManagerViewModel : EmployeeViewModel - { - public int Level { get; set; } - - public List Employees { get; set; } = new(); - } -} \ No newline at end of file diff --git a/test/MapTo.Integration.Tests/MapTo.Integration.Tests.csproj b/test/MapTo.Integration.Tests/MapTo.Integration.Tests.csproj deleted file mode 100644 index a6dba0d..0000000 --- a/test/MapTo.Integration.Tests/MapTo.Integration.Tests.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - net5.0 - false - - - - - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - diff --git a/test/TestConsoleApp/Data/FinanceTransaction.cs b/test/TestConsoleApp/Data/FinanceTransaction.cs deleted file mode 100644 index d26b5dd..0000000 --- a/test/TestConsoleApp/Data/FinanceTransaction.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using MapTo; - -namespace BlueWest.Data -{ - public enum FinanceSymbol - { - BTC_EUR, - BTC_BUSD, - BTC_USD, - BTC_USDT, - LTC_EUR, - LTC_BUSD, - LTC_USDT - - - - - - - - - - } - - public enum FinanceTransactionType - { - Buy, - Sell - } - - [JsonExtension] - [MapFrom(typeof(FinanceTransactionInsertDto))] - public partial struct FinanceTransaction - { - public readonly int Id; - public readonly int UserId; - public readonly FinanceTransactionType FinanceTransactionType; - public readonly FinanceSymbol FinanceSymbol; - public readonly double Amount; // To Buy - public readonly double Quantity; // Bought - public readonly double Fee; - public readonly DateTime DateTime; - - - public FinanceTransaction(int id, int userId, FinanceTransactionType financeTransactionType, - FinanceSymbol financeSymbol, double amount, double quantity, double fee, DateTime dateTime) - { - Id = id; - UserId = userId; - FinanceTransactionType = financeTransactionType; - FinanceSymbol = financeSymbol; - Amount = amount; - Quantity = quantity; - Fee = fee; - DateTime = dateTime; - } - } -} \ No newline at end of file diff --git a/test/TestConsoleApp/Data/FinanceTransactionInsertDto.cs b/test/TestConsoleApp/Data/FinanceTransactionInsertDto.cs deleted file mode 100644 index 3c47c56..0000000 --- a/test/TestConsoleApp/Data/FinanceTransactionInsertDto.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace BlueWest.Data -{ - - public partial struct FinanceTransactionInsertDto - { - public readonly int UserId; - public readonly FinanceTransactionType FinanceTransactionType; - public readonly FinanceSymbol FinanceSymbol; - public readonly double Amount; // To Buy - public readonly double Quantity; // Bought - public readonly double Fee; - public readonly DateTime DateTime; - } -} \ No newline at end of file diff --git a/test/TestConsoleApp/Data/FinanceTransactionReadDto.cs b/test/TestConsoleApp/Data/FinanceTransactionReadDto.cs deleted file mode 100644 index 67fdd26..0000000 --- a/test/TestConsoleApp/Data/FinanceTransactionReadDto.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using MapTo; - -namespace BlueWest.Data -{ - [MapFrom(typeof(FinanceTransaction))] - - partial struct FinanceTransactionReadDto - { - public readonly int UserId; - public readonly FinanceTransactionType FinanceTransactionType; - public readonly FinanceSymbol FinanceSymbol; - public readonly double Amount; // To Buy - public readonly double Quantity; // Bought - public readonly double Fee; - public readonly DateTime DateTime; - - public readonly string ReadData; - } -} diff --git a/test/TestConsoleApp/Data/Models/Car.cs b/test/TestConsoleApp/Data/Models/Car.cs deleted file mode 100644 index fe6f130..0000000 --- a/test/TestConsoleApp/Data/Models/Car.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using MapTo; -using TestConsoleApp.ViewModels; - -namespace TestConsoleApp.Data.Models -{ - [MapFrom(typeof(CarReadDto))] - [UseUpdate] - partial class Car - { - public int Size { get; } - public int Id { get; } - - public string Brand { get; } - - public Car(int size, int id, string brand) - { - Size = size; - Id = id; - Brand = brand; - } - } -} diff --git a/test/TestConsoleApp/Data/Models/Employee.cs b/test/TestConsoleApp/Data/Models/Employee.cs deleted file mode 100644 index a25a6d9..0000000 --- a/test/TestConsoleApp/Data/Models/Employee.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace TestConsoleApp.Data.Models -{ - - public class Employee - { - public int Id { get; } - - public string EmployeeCode { get; } - - public Employee(int id, string employeeCode) - { - Id = id; - EmployeeCode = employeeCode; - } - - } -} diff --git a/test/TestConsoleApp/Data/Models/MyStruct.cs b/test/TestConsoleApp/Data/Models/MyStruct.cs deleted file mode 100644 index 2be6f38..0000000 --- a/test/TestConsoleApp/Data/Models/MyStruct.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using TestConsoleApp.ViewModels; -using MapTo; - -namespace TestConsoleApp.Data.Models -{ - [MapFrom(typeof(MyStructViewModel))] - [UseUpdate] - public partial struct MyStruct - { - public int SomeInt { get; set; } - - public string ReadOnlyString { get; } - - public MyStruct(int someInt, string readOnlyString) - { - SomeInt = someInt; - ReadOnlyString = readOnlyString; - } - - } -} diff --git a/test/TestConsoleApp/Data/User.cs b/test/TestConsoleApp/Data/User.cs deleted file mode 100644 index 158ce46..0000000 --- a/test/TestConsoleApp/Data/User.cs +++ /dev/null @@ -1,44 +0,0 @@ - -using System.Collections.Generic; -using MapTo; - - -namespace BlueWest.Data -{ - [MapFrom(typeof(UserUpdateDto))] - [JsonExtension] - public partial class User - { - public readonly int Id; - public string Name; - public string Address; - - public string BTCAddress; - public string LTCAddress; - - public double BTCAmount; - public double LTCAmount; - - public readonly List FinanceTransactions; - - public User(int id, string name, string address, string btcAddress, string ltcAddress, double btcAmount, double ltcAmount, List financeTransactions) - { - Id = id; - Name = name; - Address = address; - BTCAddress = btcAddress; - LTCAddress = ltcAddress; - BTCAmount = btcAmount; - LTCAmount = ltcAmount; - FinanceTransactions = financeTransactions; - } - - public void AddTransaction(FinanceTransaction financeTransaction) - { - FinanceTransactions.Add(financeTransaction); - } - - } -} - - diff --git a/test/TestConsoleApp/Data/UserList.cs b/test/TestConsoleApp/Data/UserList.cs deleted file mode 100644 index b653d18..0000000 --- a/test/TestConsoleApp/Data/UserList.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Generic; - -namespace BlueWest.Data -{ - public class UserList - { - public List Users; - - public UserList(List users) - { - Users = users; - } - - public int Length => Users.Count; - } -} \ No newline at end of file diff --git a/test/TestConsoleApp/Data/UserUpdateDto.cs b/test/TestConsoleApp/Data/UserUpdateDto.cs deleted file mode 100644 index a1b18d3..0000000 --- a/test/TestConsoleApp/Data/UserUpdateDto.cs +++ /dev/null @@ -1,19 +0,0 @@ -using MapTo; - -namespace BlueWest.Data -{ - [MapFrom(typeof(User))] - - public partial class UserUpdateDto - { - public string Name; - public string Address; - - public string BTCAddress; - public string LTCAddress; - - public double BTCAmount; - public double LTCAmount; - - } -} \ No newline at end of file diff --git a/test/TestConsoleApp/Program.cs b/test/TestConsoleApp/Program.cs deleted file mode 100644 index 06a1bc2..0000000 --- a/test/TestConsoleApp/Program.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using MapTo; -using TestConsoleApp.Data.Models; -using TestConsoleApp.ViewModels; - -namespace TestConsoleApp -{ - internal class Program - { - private static void Main(string[] args) - { - //UserTest(); - - // EmployeeManagerTest(); - Console.WriteLine("done"); - } - - private static void EmployeeManagerTest() - { - - - var employee = new Employee(1, "hello"); - - - - - } - - - - } -} \ No newline at end of file diff --git a/test/TestConsoleApp/TestConsoleApp.csproj b/test/TestConsoleApp/TestConsoleApp.csproj deleted file mode 100644 index c71e8de..0000000 --- a/test/TestConsoleApp/TestConsoleApp.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - Exe - net471 - latest - enable - - - - - - - - - Internal - - diff --git a/test/TestConsoleApp/ViewModels/CarReadDto.cs b/test/TestConsoleApp/ViewModels/CarReadDto.cs deleted file mode 100644 index 2241eff..0000000 --- a/test/TestConsoleApp/ViewModels/CarReadDto.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using MapTo; -using TestConsoleApp.Data.Models; - -namespace TestConsoleApp.ViewModels -{ - [MapFrom(typeof(Car))] - partial class CarReadDto - { - public int Size { get; } - public string Brand { get; } - - public CarReadDto(int size, string brand) - { - Size = size; - Brand = brand; - } - } -} diff --git a/test/TestConsoleApp/ViewModels/EmployeeViewModel.cs b/test/TestConsoleApp/ViewModels/EmployeeViewModel.cs deleted file mode 100644 index 6c3adf4..0000000 --- a/test/TestConsoleApp/ViewModels/EmployeeViewModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using MapTo; -using TestConsoleApp.Data.Models; - -namespace TestConsoleApp.ViewModels -{ - [MapFrom(typeof(Employee))] - public partial class EmployeeViewModel - { - public int Id { get; } - - - } -} diff --git a/test/TestConsoleApp/ViewModels/MyStructViewModel.cs b/test/TestConsoleApp/ViewModels/MyStructViewModel.cs deleted file mode 100644 index 46577ea..0000000 --- a/test/TestConsoleApp/ViewModels/MyStructViewModel.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using TestConsoleApp.Data.Models; -using MapTo; - -namespace TestConsoleApp.ViewModels -{ - [MapFrom(typeof(MyStruct))] - - public partial struct MyStructViewModel - { - public int SomeInt { get; set; } - - public MyStructViewModel(int someInt) - { - SomeInt = someInt; - } - } -}