From 8280822242344d82e92b145c2225ab499824ba3c Mon Sep 17 00:00:00 2001 From: ladeak Date: Wed, 9 Oct 2024 22:48:38 +0200 Subject: [PATCH] Incremental compilesIncremental re-write part 1 --- .../AspNetCoreMinimal.Entities.csproj | 1 + sample/AspNetCoreMinimal.Entities/Entities.cs | 4 +- .../AspNetCoreMinimal.csproj | 1 + .../Controllers/SampleController.cs | 6 +- sample/AspNetCoreMinimal/Program.cs | 14 +- sample/ConsoleApp/ConsoleApp.csproj | 1 + sample/ConsoleApp/Program.cs | 52 +- .../ConsoleAppLibrary.csproj | 1 + sample/ConsoleAppLibrary/Sample.cs | 36 +- .../ITypeRepository.cs | 4 +- .../ITypeRepositoryExtensions.cs | 6 +- .../JsonMergePatch.Abstractions.csproj | 1 + src/JsonMergePatch.Abstractions/Patch.cs | 2 +- .../PatchableAttribute.cs | 4 +- .../JsonMergePatch.AspNetCore.csproj | 1 + .../JsonMergePatchInputReader.cs | 6 +- .../HttpContentExtensions.cs | 15 +- .../JsonMergePatch.Http.csproj | 1 + .../ApplyPatchBuilders/ApplyPatchBuilder.cs | 4 +- .../GeneratableListBuilder.cs | 19 +- .../NonGeneratableDictionaryPatchBuilder.cs | 22 +- .../SimpleGeneratableBuilder.cs | 20 +- .../SimpleNonGeneratableBuilder.cs | 30 +- .../BuilderState.cs | 44 +- src/JsonMergePatch.SourceGenerator/Casing.cs | 32 +- .../GeneratedTypeFilter.cs | 93 ++- .../GeneratedWrapper.cs | 17 +- .../IEnumerableExtensions.cs | 20 +- .../IPatchParametersWalker.cs | 3 +- .../ITypeBuilder.cs | 9 +- .../ITypeBuilderGenerator.cs | 9 +- .../JsonMergePatch.SourceGenerator.csproj | 8 +- .../JsonMergePatchSourceGenerator.cs | 44 +- .../MultiTypeBuilder.cs | 59 -- .../PatchParametersWalker.cs | 5 +- .../StringBuilderExtensions.cs | 15 +- .../TypeBuilder.cs | 302 ++++--- .../TypeInformation.cs | 20 - .../TypeRepositoryGenerator.cs | 25 +- .../HttpContentExtensionsTests.cs | 21 +- .../JsonMergePatch.Http.Tests.csproj | 7 +- test/JsonMergePatch.Http.Tests/TestDto.cs | 9 +- .../TestDtoWrapped.cs | 29 +- .../BasicSerializationTests.cs | 10 + .../BuilderStateTests.cs | 127 ++- .../DictionarySerializationTests.cs | 7 + .../GeneratedWrapperTypeTests.cs | 2 + .../IEnumerableExtensionsTests.cs | 99 ++- ...sonMergePatch.SourceGenerator.Tests.csproj | 10 +- .../JsonMergePatchSourceGeneratorTests.cs | 15 + .../MultiTypeBuilderTests.cs | 189 ----- .../NameBuilderTests.cs | 88 +- .../TypeBuilderTests.cs | 763 ++++++++---------- .../TypeRepositoryGeneratorTests.cs | 215 +++-- .../TypesWithCtorSerializationTests.cs | 2 + .../JsonMergePatch.AspNetCore.Tests.csproj | 7 +- .../JsonMergePatchInputReaderTests.cs | 21 +- test/JsonMergePatch.Tests/TestDto.cs | 9 +- test/JsonMergePatch.Tests/TestDtoWrapped.cs | 29 +- 59 files changed, 1150 insertions(+), 1465 deletions(-) delete mode 100644 src/JsonMergePatch.SourceGenerator/MultiTypeBuilder.cs delete mode 100644 src/JsonMergePatch.SourceGenerator/TypeInformation.cs delete mode 100644 test/JsonMergePatch.SourceGenerator.Abstractions.Tests/MultiTypeBuilderTests.cs diff --git a/sample/AspNetCoreMinimal.Entities/AspNetCoreMinimal.Entities.csproj b/sample/AspNetCoreMinimal.Entities/AspNetCoreMinimal.Entities.csproj index 7a30cb4..63beccf 100644 --- a/sample/AspNetCoreMinimal.Entities/AspNetCoreMinimal.Entities.csproj +++ b/sample/AspNetCoreMinimal.Entities/AspNetCoreMinimal.Entities.csproj @@ -4,6 +4,7 @@ net8.0 enable false + true diff --git a/sample/AspNetCoreMinimal.Entities/Entities.cs b/sample/AspNetCoreMinimal.Entities/Entities.cs index 7bd1c40..7eff814 100644 --- a/sample/AspNetCoreMinimal.Entities/Entities.cs +++ b/sample/AspNetCoreMinimal.Entities/Entities.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; using LaDeak.JsonMergePatch.Abstractions; namespace AspNetCoreMinimal.Entities; diff --git a/sample/AspNetCoreMinimal/AspNetCoreMinimal.csproj b/sample/AspNetCoreMinimal/AspNetCoreMinimal.csproj index 9c386ca..5a5e195 100644 --- a/sample/AspNetCoreMinimal/AspNetCoreMinimal.csproj +++ b/sample/AspNetCoreMinimal/AspNetCoreMinimal.csproj @@ -4,6 +4,7 @@ net8.0 enable false + true diff --git a/sample/AspNetCoreMinimal/Controllers/SampleController.cs b/sample/AspNetCoreMinimal/Controllers/SampleController.cs index 247039a..16dbf5c 100644 --- a/sample/AspNetCoreMinimal/Controllers/SampleController.cs +++ b/sample/AspNetCoreMinimal/Controllers/SampleController.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Text.Json; -using System.Threading.Tasks; +using System.Text.Json; using AspNetCoreMinimal.Entities; using LaDeak.JsonMergePatch.Abstractions; using LaDeak.JsonMergePatch.Http; diff --git a/sample/AspNetCoreMinimal/Program.cs b/sample/AspNetCoreMinimal/Program.cs index 6903614..64f876e 100644 --- a/sample/AspNetCoreMinimal/Program.cs +++ b/sample/AspNetCoreMinimal/Program.cs @@ -1,32 +1,22 @@ using System.Text.Json.Serialization; using LaDeak.JsonMergePatch.AspNetCore; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; var builder = WebApplication.CreateBuilder(args); var mvcBuilder = builder.Services.AddControllers().AddMvcOptions(options => { - LaDeak.JsonMergePatch.Abstractions.JsonMergePatchOptions.Repository = LaDeak.JsonMergePatch.Generated.SafeAspNetCoreMinimal.Entities.TypeRepository.Instance; + LaDeak.JsonMergePatch.Abstractions.JsonMergePatchOptions.Repository = LaDeak.JsonMergePatch.Generated.SafeAspNetCoreMinimal.TypeRepository.Instance; var jsonOptions = new Microsoft.AspNetCore.Http.Json.JsonOptions(); - jsonOptions.SerializerOptions.AddContext(); + jsonOptions.SerializerOptions.TypeInfoResolver = SampleJsonContext.Default; options.InputFormatters.Insert(0, new JsonMergePatchInputReader(jsonOptions)); }); builder.Services.AddHttpClient(); - - var app = builder.Build(); - app.UseHttpsRedirection(); - app.UseAuthorization(); - app.MapControllers(); - app.Run(); - [JsonSerializable(typeof(LaDeak.JsonMergePatch.Generated.SafeAspNetCoreMinimal.Entities.WeatherForecastWrapped))] [JsonSerializable(typeof(LaDeak.JsonMergePatch.Generated.SafeAspNetCoreMinimal.Entities.CitiesDataWrapped))] public partial class SampleJsonContext : JsonSerializerContext diff --git a/sample/ConsoleApp/ConsoleApp.csproj b/sample/ConsoleApp/ConsoleApp.csproj index 154843d..b47032a 100644 --- a/sample/ConsoleApp/ConsoleApp.csproj +++ b/sample/ConsoleApp/ConsoleApp.csproj @@ -4,6 +4,7 @@ Exe net8.0 false + true diff --git a/sample/ConsoleApp/Program.cs b/sample/ConsoleApp/Program.cs index e3a8226..ea628f9 100644 --- a/sample/ConsoleApp/Program.cs +++ b/sample/ConsoleApp/Program.cs @@ -1,38 +1,34 @@ -using System; -using System.Net.Http; -using System.Threading.Tasks; -using ConsoleAppLibrary; +using ConsoleAppLibrary; using LaDeak.JsonMergePatch.Abstractions; using LaDeak.JsonMergePatch.Http; -namespace ReadJsonPatchAsync +namespace ReadJsonPatchAsync; + +public class Program { - public class Program + public class WeatherForecast { - public class WeatherForecast - { - public DateTime Date { get; set; } - public int Temp { get; set; } - public string Summary { get; set; } - } + public DateTime Date { get; set; } + public int Temp { get; set; } + public string Summary { get; set; } + } - public static async Task Main(string[] args) - { - LaDeak.JsonMergePatch.Abstractions.JsonMergePatchOptions.Repository = LaDeak.JsonMergePatch.Generated.SafeConsoleApp.TypeRepository.Instance.Extend(LaDeak.JsonMergePatch.Generated.SafeConsoleAppLibrary.TypeRepository.Instance); - await ReadAsJsonMergePatchAsync(); - } + public static async Task Main(string[] args) + { + LaDeak.JsonMergePatch.Abstractions.JsonMergePatchOptions.Repository = LaDeak.JsonMergePatch.Generated.Safe.TypeRepository.Instance.Extend(LaDeak.JsonMergePatch.Generated.SafeConsoleAppLibrary.TypeRepository.Instance); + await ReadAsJsonMergePatchAsync(); + } - public static async Task ReadAsJsonMergePatchAsync() - { - var httpClient = new HttpClient(); - var response = await httpClient.GetAsync("https://localhost:5001/Sample/Weather", HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); - var responseData = await response.Content.ReadJsonPatchAsync().ConfigureAwait(false); - var target = new WeatherForecast() { Date = DateTime.UtcNow, Summary = "Sample weather forecast", Temp = 24 }; - var result = responseData.ApplyPatch(target); - Console.WriteLine($"Patched: Date={result.Date}, Summary={result.Summary}, Temp={result.Temp}"); + public static async Task ReadAsJsonMergePatchAsync() + { + var httpClient = new HttpClient(); + var response = await httpClient.GetAsync("https://localhost:5001/Sample/Weather", HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); + var responseData = await response.Content.ReadJsonPatchAsync().ConfigureAwait(false); + var target = new WeatherForecast() { Date = DateTime.UtcNow, Summary = "Sample weather forecast", Temp = 24 }; + var result = responseData.ApplyPatch(target); + Console.WriteLine($"Patched: Date={result.Date}, Summary={result.Summary}, Temp={result.Temp}"); - var client = new Client(); - await client.ReadAsJsonMergePatchAsync(); - } + var client = new Client(); + await client.ReadAsJsonMergePatchAsync(); } } diff --git a/sample/ConsoleAppLibrary/ConsoleAppLibrary.csproj b/sample/ConsoleAppLibrary/ConsoleAppLibrary.csproj index 8303415..0c3dda1 100644 --- a/sample/ConsoleAppLibrary/ConsoleAppLibrary.csproj +++ b/sample/ConsoleAppLibrary/ConsoleAppLibrary.csproj @@ -3,6 +3,7 @@ net8.0 false + true diff --git a/sample/ConsoleAppLibrary/Sample.cs b/sample/ConsoleAppLibrary/Sample.cs index 14b3b93..3d66723 100644 --- a/sample/ConsoleAppLibrary/Sample.cs +++ b/sample/ConsoleAppLibrary/Sample.cs @@ -1,26 +1,22 @@ -using System; -using System.Net.Http; -using System.Threading.Tasks; -using LaDeak.JsonMergePatch.Http; +using LaDeak.JsonMergePatch.Http; -namespace ConsoleAppLibrary +namespace ConsoleAppLibrary; + +public class DeviceData { - public class DeviceData - { - public double Watts { get; set; } - public string Name { get; set; } - } + public double Watts { get; set; } + public string Name { get; set; } +} - public class Client +public class Client +{ + public async Task ReadAsJsonMergePatchAsync() { - public async Task ReadAsJsonMergePatchAsync() - { - var httpClient = new HttpClient(); - var response = await httpClient.GetAsync("https://localhost:5001/Sample/DeviceData", HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); - var responseData = await response.Content.ReadJsonPatchAsync().ConfigureAwait(false); - var target = new DeviceData() { Watts = 5, Name = "phone" }; - var result = responseData.ApplyPatch(target); - Console.WriteLine($"Patched: Name={result.Name}, Watts={result.Watts}"); - } + var httpClient = new HttpClient(); + var response = await httpClient.GetAsync("https://localhost:5001/Sample/DeviceData", HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false); + var responseData = await response.Content.ReadJsonPatchAsync().ConfigureAwait(false); + var target = new DeviceData() { Watts = 5, Name = "phone" }; + var result = responseData.ApplyPatch(target); + Console.WriteLine($"Patched: Name={result.Name}, Watts={result.Watts}"); } } diff --git a/src/JsonMergePatch.Abstractions/ITypeRepository.cs b/src/JsonMergePatch.Abstractions/ITypeRepository.cs index 9fd0d4b..36e95f8 100644 --- a/src/JsonMergePatch.Abstractions/ITypeRepository.cs +++ b/src/JsonMergePatch.Abstractions/ITypeRepository.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; namespace LaDeak.JsonMergePatch.Abstractions; diff --git a/src/JsonMergePatch.Abstractions/ITypeRepositoryExtensions.cs b/src/JsonMergePatch.Abstractions/ITypeRepositoryExtensions.cs index 049ff89..5830d0f 100644 --- a/src/JsonMergePatch.Abstractions/ITypeRepositoryExtensions.cs +++ b/src/JsonMergePatch.Abstractions/ITypeRepositoryExtensions.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace LaDeak.JsonMergePatch.Abstractions; +namespace LaDeak.JsonMergePatch.Abstractions; public static class ITypeRepositoryExtensions { diff --git a/src/JsonMergePatch.Abstractions/JsonMergePatch.Abstractions.csproj b/src/JsonMergePatch.Abstractions/JsonMergePatch.Abstractions.csproj index efc1c31..d66a216 100644 --- a/src/JsonMergePatch.Abstractions/JsonMergePatch.Abstractions.csproj +++ b/src/JsonMergePatch.Abstractions/JsonMergePatch.Abstractions.csproj @@ -4,6 +4,7 @@ netstandard2.1 LaDeak.JsonMergePatch.Abstractions enable + true diff --git a/src/JsonMergePatch.Abstractions/Patch.cs b/src/JsonMergePatch.Abstractions/Patch.cs index e310d4d..9a8ff68 100644 --- a/src/JsonMergePatch.Abstractions/Patch.cs +++ b/src/JsonMergePatch.Abstractions/Patch.cs @@ -7,7 +7,7 @@ public abstract class Patch /// /// List of properties used by the request. /// - protected bool[] Properties; + protected bool[] Properties = []; /// /// Applies updates on input type using Json Merge Patch rules. diff --git a/src/JsonMergePatch.Abstractions/PatchableAttribute.cs b/src/JsonMergePatch.Abstractions/PatchableAttribute.cs index 247589f..5768232 100644 --- a/src/JsonMergePatch.Abstractions/PatchableAttribute.cs +++ b/src/JsonMergePatch.Abstractions/PatchableAttribute.cs @@ -1,6 +1,4 @@ -using System; - -namespace LaDeak.JsonMergePatch.Abstractions; +namespace LaDeak.JsonMergePatch.Abstractions; [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class)] public class PatchableAttribute : Attribute diff --git a/src/JsonMergePatch.AspNetCore/JsonMergePatch.AspNetCore.csproj b/src/JsonMergePatch.AspNetCore/JsonMergePatch.AspNetCore.csproj index a472b33..068e3ff 100644 --- a/src/JsonMergePatch.AspNetCore/JsonMergePatch.AspNetCore.csproj +++ b/src/JsonMergePatch.AspNetCore/JsonMergePatch.AspNetCore.csproj @@ -4,6 +4,7 @@ net8.0 LaDeak.JsonMergePatch.AspNetCore enable + true diff --git a/src/JsonMergePatch.AspNetCore/JsonMergePatchInputReader.cs b/src/JsonMergePatch.AspNetCore/JsonMergePatchInputReader.cs index 6ea96c0..e3feb04 100644 --- a/src/JsonMergePatch.AspNetCore/JsonMergePatchInputReader.cs +++ b/src/JsonMergePatch.AspNetCore/JsonMergePatchInputReader.cs @@ -1,9 +1,5 @@ -using System; -using System.IO; -using System.Linq; -using System.Text; +using System.Text; using System.Text.Json; -using System.Threading.Tasks; using LaDeak.JsonMergePatch.Abstractions; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Json; diff --git a/src/JsonMergePatch.Http/HttpContentExtensions.cs b/src/JsonMergePatch.Http/HttpContentExtensions.cs index cf825c7..d8f8384 100644 --- a/src/JsonMergePatch.Http/HttpContentExtensions.cs +++ b/src/JsonMergePatch.Http/HttpContentExtensions.cs @@ -1,9 +1,5 @@ -using System; -using System.Net.Http; -using System.Text; +using System.Text; using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; using LaDeak.JsonMergePatch.Abstractions; namespace LaDeak.JsonMergePatch.Http; @@ -16,21 +12,18 @@ public static class HttpContentExtensions public static async Task?> ReadJsonPatchAsync(this HttpContent content, ITypeRepository typeRepository, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default) { - if (content == null) - throw new ArgumentNullException(nameof(content)); - if (typeRepository == null) - throw new ArgumentNullException(nameof(typeRepository)); + ArgumentNullException.ThrowIfNull(content); + ArgumentNullException.ThrowIfNull(typeRepository); if (!typeRepository.TryGet(typeof(TResult), out var wrapperType)) throw new ArgumentException($"{typeof(TResult)} is missing generated wrapper type. Check if all members of the type definition is supported."); if (content.Headers.ContentType?.MediaType != "application/merge-patch+json" && content.Headers.ContentType?.MediaType != "application/json" && !string.IsNullOrWhiteSpace(content.Headers.ContentType?.MediaType)) return null; var contentStream = await content.ReadAsStreamAsync().ConfigureAwait(false); -#if NET5_0_OR_GREATER Encoding? encoding = content.Headers.ContentType?.CharSet != null ? GetEncoding(content.Headers.ContentType.CharSet) : null; if (encoding != null && encoding != Encoding.UTF8) contentStream = Encoding.CreateTranscodingStream(contentStream, encoding, Encoding.UTF8); -#endif + await using (contentStream.ConfigureAwait(false)) { var contentData = await JsonSerializer.DeserializeAsync(contentStream, wrapperType, options ?? _serializerOptions, cancellationToken).ConfigureAwait(false); diff --git a/src/JsonMergePatch.Http/JsonMergePatch.Http.csproj b/src/JsonMergePatch.Http/JsonMergePatch.Http.csproj index 88f0806..500edb1 100644 --- a/src/JsonMergePatch.Http/JsonMergePatch.Http.csproj +++ b/src/JsonMergePatch.Http/JsonMergePatch.Http.csproj @@ -4,6 +4,7 @@ net8.0 LaDeak.JsonMergePatch.Http enable + true diff --git a/src/JsonMergePatch.SourceGenerator/ApplyPatchBuilders/ApplyPatchBuilder.cs b/src/JsonMergePatch.SourceGenerator/ApplyPatchBuilders/ApplyPatchBuilder.cs index 51673ac..a9f91c2 100644 --- a/src/JsonMergePatch.SourceGenerator/ApplyPatchBuilders/ApplyPatchBuilder.cs +++ b/src/JsonMergePatch.SourceGenerator/ApplyPatchBuilders/ApplyPatchBuilder.cs @@ -3,7 +3,7 @@ public abstract class ApplyPatchBuilder { /// - /// Generates logic to instantiate object for init property. + /// Generates logic to instantiate object with init property. /// /// The state representing the ApplyPatch method. /// The index of the property in the 'Properties' collection. @@ -11,7 +11,7 @@ public abstract class ApplyPatchBuilder public abstract BuilderState BuildInitOnly(BuilderState state, int i); /// - /// Generates logic to instantiate object for non-init property. + /// Generates logic to instantiate object with non-init property. /// /// The state representing the ApplyPatch method. /// The index of the property in the 'Properties' collection. diff --git a/src/JsonMergePatch.SourceGenerator/ApplyPatchBuilders/GeneratableListBuilder.cs b/src/JsonMergePatch.SourceGenerator/ApplyPatchBuilders/GeneratableListBuilder.cs index 3bc6894..b20211b 100644 --- a/src/JsonMergePatch.SourceGenerator/ApplyPatchBuilders/GeneratableListBuilder.cs +++ b/src/JsonMergePatch.SourceGenerator/ApplyPatchBuilders/GeneratableListBuilder.cs @@ -1,6 +1,4 @@ -using System; -using System.Linq; -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; namespace LaDeak.JsonMergePatch.SourceGenerator.ApplyPatchBuilders; @@ -10,9 +8,9 @@ namespace LaDeak.JsonMergePatch.SourceGenerator.ApplyPatchBuilders; public class GeneratableListBuilder : ApplyPatchBuilder { private readonly ITypeSymbol _firstGenericType; - private readonly IPropertySymbol _property; + private readonly string _propertyName; - public GeneratableListBuilder(INamedTypeSymbol namedType, IPropertySymbol property) + public GeneratableListBuilder(INamedTypeSymbol namedType, string propertyName) { if (!namedType.Name.Contains("List") || namedType.ContainingNamespace.ToDisplayString() != "System.Collections.Generic" @@ -21,32 +19,31 @@ public GeneratableListBuilder(INamedTypeSymbol namedType, IPropertySymbol proper throw new ArgumentException("Input argument type is not a generic list with generatable type."); } _firstGenericType = namedType.TypeArguments.First(); - _property = property; + _propertyName = propertyName; } public override BuilderState BuildInitOnly(BuilderState state, int i) { - state.AppendLine($"{_property.Name} = Properties[{i}] && input.{_property.Name} == null ? new() : input.{_property.Name},"); + state.AppendLine($"{_propertyName} = Properties[{i}] && input.{_propertyName} == null ? new() : input.{_propertyName},"); return state; } public override BuilderState BuildInstantiation(BuilderState state, int i) { state.AppendLine($"if (Properties[{i}])"); - state.IncrementIdentation().AppendLine($"input.{_property.Name} = new();"); + state.IncrementIdentation().AppendLine($"input.{_propertyName} = new();"); return state; } public override BuilderState BuildPatch(BuilderState state) { if (_firstGenericType != null && GeneratedTypeFilter.IsGeneratableType(_firstGenericType)) - PopulateGeneratableListProperties(state, _property); + PopulateGeneratableListProperties(state, _propertyName); return state; } - private void PopulateGeneratableListProperties(BuilderState state, IPropertySymbol property) + private void PopulateGeneratableListProperties(BuilderState state, string propertyName) { - var propertyName = property.Name; state.AppendLine($"if({propertyName} != null)"); state.AppendLine("{"); var ifBody = state.IncrementIdentation(); diff --git a/src/JsonMergePatch.SourceGenerator/ApplyPatchBuilders/NonGeneratableDictionaryPatchBuilder.cs b/src/JsonMergePatch.SourceGenerator/ApplyPatchBuilders/NonGeneratableDictionaryPatchBuilder.cs index b73e96a..539d85a 100644 --- a/src/JsonMergePatch.SourceGenerator/ApplyPatchBuilders/NonGeneratableDictionaryPatchBuilder.cs +++ b/src/JsonMergePatch.SourceGenerator/ApplyPatchBuilders/NonGeneratableDictionaryPatchBuilder.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; namespace LaDeak.JsonMergePatch.SourceGenerator.ApplyPatchBuilders; @@ -9,41 +8,42 @@ namespace LaDeak.JsonMergePatch.SourceGenerator.ApplyPatchBuilders; public class NonGeneratableDictionaryPatchBuilder : ApplyPatchBuilder { private readonly INamedTypeSymbol _namedType; - private readonly IPropertySymbol _property; + private readonly string _propertyName; + private readonly bool _hasGeneratableType; private readonly bool _isConvertedToNullableType; - public NonGeneratableDictionaryPatchBuilder(INamedTypeSymbol namedType, IPropertySymbol property, bool isConvertedToNullableType) + public NonGeneratableDictionaryPatchBuilder(INamedTypeSymbol namedType, string propertyName, bool hasGeneratableType, bool isConvertedToNullableType) { if (!namedType.Name.Contains("Dictionary") || namedType.ContainingNamespace.ToDisplayString() != "System.Collections.Generic") throw new ArgumentException($"Input argument type is not a valid for {nameof(NonGeneratableDictionaryPatchBuilder)}"); _namedType = namedType; - _property = property; + _propertyName = propertyName; + _hasGeneratableType = hasGeneratableType; _isConvertedToNullableType = isConvertedToNullableType; } public override BuilderState BuildInitOnly(BuilderState state, int i) { - state.AppendLine($"{_property.Name} = Properties[{i}] && input.{_property.Name} == null ? new() : input.Values,"); + state.AppendLine($"{_propertyName} = Properties[{i}] && input.{_propertyName} == null ? new() : input.Values,"); return state; } public override BuilderState BuildInstantiation(BuilderState state, int i) { state.AppendLine($"if (Properties[{i}])"); - state.IncrementIdentation().AppendLine($"input.{_property.Name} ??= new();"); + state.IncrementIdentation().AppendLine($"input.{_propertyName} ??= new();"); return state; } public override BuilderState BuildPatch(BuilderState state) { - if (!GeneratedTypeFilter.IsGeneratableType(_property.Type)) - PopulateDictionary(state, _property); + if (!_hasGeneratableType) + PopulateDictionary(state, _propertyName); return state; } - private void PopulateDictionary(BuilderState state, IPropertySymbol property) + private void PopulateDictionary(BuilderState state, string propertyName) { - var propertyName = property.Name; state.AppendLine($"if({propertyName} != null)"); state.AppendLine("{"); var ifBody = state.IncrementIdentation(); diff --git a/src/JsonMergePatch.SourceGenerator/ApplyPatchBuilders/SimpleGeneratableBuilder.cs b/src/JsonMergePatch.SourceGenerator/ApplyPatchBuilders/SimpleGeneratableBuilder.cs index f3c8626..99c5676 100644 --- a/src/JsonMergePatch.SourceGenerator/ApplyPatchBuilders/SimpleGeneratableBuilder.cs +++ b/src/JsonMergePatch.SourceGenerator/ApplyPatchBuilders/SimpleGeneratableBuilder.cs @@ -1,32 +1,20 @@ -using System; -using Microsoft.CodeAnalysis; - -namespace LaDeak.JsonMergePatch.SourceGenerator.ApplyPatchBuilders; +namespace LaDeak.JsonMergePatch.SourceGenerator.ApplyPatchBuilders; /// /// Builds ApplyPatch method for user types where a Wrapper DTO is generated. /// -public class SimpleGeneratableBuilder : ApplyPatchBuilder +public class SimpleGeneratableBuilder(string name) : ApplyPatchBuilder { - private readonly IPropertySymbol _propertySymbol; - - public SimpleGeneratableBuilder(IPropertySymbol propertySymbol) - { - if (!GeneratedTypeFilter.IsGeneratableType(propertySymbol.Type)) - throw new ArgumentException("The given property is not simple generatable"); - _propertySymbol = propertySymbol; - } - public override BuilderState BuildInitOnly(BuilderState state, int i) { - state.AppendLine($"{_propertySymbol.Name} = Properties[{i}] ? this.{_propertySymbol.Name}?.ApplyPatch(input.{_propertySymbol.Name}) : input.{_propertySymbol.Name},"); + state.AppendLine($"{name} = Properties[{i}] ? this.{name}?.ApplyPatch(input.{name}) : input.{name},"); return state; } public override BuilderState BuildInstantiation(BuilderState state, int i) { state.AppendLine($"if (Properties[{i}])"); - state.IncrementIdentation().AppendLine($"input.{_propertySymbol.Name} = {_propertySymbol.Name}?.ApplyPatch(input.{_propertySymbol.Name});"); + state.IncrementIdentation().AppendLine($"input.{name} = {name}?.ApplyPatch(input.{name});"); return state; } diff --git a/src/JsonMergePatch.SourceGenerator/ApplyPatchBuilders/SimpleNonGeneratableBuilder.cs b/src/JsonMergePatch.SourceGenerator/ApplyPatchBuilders/SimpleNonGeneratableBuilder.cs index dfcb346..9829c09 100644 --- a/src/JsonMergePatch.SourceGenerator/ApplyPatchBuilders/SimpleNonGeneratableBuilder.cs +++ b/src/JsonMergePatch.SourceGenerator/ApplyPatchBuilders/SimpleNonGeneratableBuilder.cs @@ -1,40 +1,26 @@ -using System; -using Microsoft.CodeAnalysis; - -namespace LaDeak.JsonMergePatch.SourceGenerator.ApplyPatchBuilders; +namespace LaDeak.JsonMergePatch.SourceGenerator.ApplyPatchBuilders; /// /// Builds ApplyPatch method for types where no wrapper is generated, or the type is nullable. /// -public class SimpleNonGeneratableBuilder : ApplyPatchBuilder +public class SimpleNonGeneratableBuilder(string name, bool isConvertedToNullable) : ApplyPatchBuilder { - private readonly IPropertySymbol _propertySymbol; - private readonly bool _isConvertedToNullable; - - public SimpleNonGeneratableBuilder(IPropertySymbol propertySymbol, bool isConvertedToNullable) - { - if (GeneratedTypeFilter.IsGeneratableType(propertySymbol.Type)) - throw new ArgumentException("The given property is not simple generatable"); - _propertySymbol = propertySymbol; - _isConvertedToNullable = isConvertedToNullable; - } - public override BuilderState BuildInitOnly(BuilderState state, int i) { - if (_isConvertedToNullable) - state.AppendLine($"{_propertySymbol.Name} = Properties[{i}] ? ({_propertySymbol.Name}.HasValue ? this.{_propertySymbol.Name}.Value : default) : input.{_propertySymbol.Name},"); + if (isConvertedToNullable) + state.AppendLine($"{name} = Properties[{i}] ? ({name}.HasValue ? this.{name}.Value : default) : input.{name},"); else - state.AppendLine($"{_propertySymbol.Name} = Properties[{i}] ? this.{_propertySymbol.Name} : input.{_propertySymbol.Name},"); + state.AppendLine($"{name} = Properties[{i}] ? this.{name} : input.{name},"); return state; } public override BuilderState BuildInstantiation(BuilderState state, int i) { state.AppendLine($"if (Properties[{i}])"); - if (_isConvertedToNullable) - state.IncrementIdentation().AppendLine($"input.{_propertySymbol.Name} = {_propertySymbol.Name}.HasValue ? {_propertySymbol.Name}.Value : default;"); + if (isConvertedToNullable) + state.IncrementIdentation().AppendLine($"input.{name} = {name}.HasValue ? {name}.Value : default;"); else - state.IncrementIdentation().AppendLine($"input.{_propertySymbol.Name} = {_propertySymbol.Name};"); + state.IncrementIdentation().AppendLine($"input.{name} = {name};"); return state; } diff --git a/src/JsonMergePatch.SourceGenerator/BuilderState.cs b/src/JsonMergePatch.SourceGenerator/BuilderState.cs index 252d77f..e7f70c3 100644 --- a/src/JsonMergePatch.SourceGenerator/BuilderState.cs +++ b/src/JsonMergePatch.SourceGenerator/BuilderState.cs @@ -1,33 +1,27 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Microsoft.CodeAnalysis; +using System.Text; -namespace LaDeak.JsonMergePatch.SourceGenerator +namespace LaDeak.JsonMergePatch.SourceGenerator; + +public class BuilderState { - public class BuilderState - { - public StringBuilder Builder { get; } - public int Identation { get; } - public TypeInformation TypeInfo { get; } - public List ToProcessTypeSymbols { get; } + public StringBuilder Builder { get; } + public int Identation { get; } + public GeneratorClassInfo TypeInfo { get; } - public BuilderState(TypeInformation typeInfo) : this(new StringBuilder(), 0, typeInfo, new List()) - { - } + public BuilderState(GeneratorClassInfo typeInfo) : this(new StringBuilder(), 0, typeInfo) + { + } - private BuilderState(StringBuilder builder, int identation, TypeInformation typeInfo, List toProcessTypeSymbols) - { - Builder = builder ?? throw new ArgumentNullException(nameof(builder)); - Identation = identation; - TypeInfo = typeInfo ?? throw new ArgumentNullException(nameof(typeInfo)); - ToProcessTypeSymbols = toProcessTypeSymbols; - } + private BuilderState(StringBuilder builder, int identation, GeneratorClassInfo typeInfo) + { + Builder = builder ?? throw new ArgumentNullException(nameof(builder)); + Identation = identation; + TypeInfo = typeInfo ?? throw new ArgumentNullException(nameof(typeInfo)); + } - public BuilderState IncrementIdentation() => new BuilderState(Builder, Identation + 4, TypeInfo, ToProcessTypeSymbols); + public BuilderState IncrementIdentation() => new BuilderState(Builder, Identation + 4, TypeInfo); - public void AppendLine(string line) => Builder.AppendLineWithIdentation(Identation, line); + public void AppendLine(string line) => Builder.AppendLineWithIdentation(Identation, line); - public void AppendLine() => Builder.AppendLine(); - } + public void AppendLine() => Builder.AppendLine(); } diff --git a/src/JsonMergePatch.SourceGenerator/Casing.cs b/src/JsonMergePatch.SourceGenerator/Casing.cs index 9363a29..2d85d55 100644 --- a/src/JsonMergePatch.SourceGenerator/Casing.cs +++ b/src/JsonMergePatch.SourceGenerator/Casing.cs @@ -1,22 +1,20 @@ -using System; -using System.Text; +using System.Text; -namespace LaDeak.JsonMergePatch.SourceGenerator +namespace LaDeak.JsonMergePatch.SourceGenerator; + +public class Casing { - public class Casing - { - [ThreadStatic] - public static StringBuilder? _builder; + [ThreadStatic] + public static StringBuilder? _builder; - public static string PrefixUnderscoreCamelCase(string name) - { - _builder ??= new StringBuilder(); - _builder.Clear(); - _builder.Append('_'); - _builder.Append(char.ToLowerInvariant(name[0])); - for (int i = 1; i < name.Length; i++) - _builder.Append(name[i]); - return _builder.ToString(); - } + public static string PrefixUnderscoreCamelCase(string name) + { + _builder ??= new StringBuilder(); + _builder.Clear(); + _builder.Append('_'); + _builder.Append(char.ToLowerInvariant(name[0])); + for (int i = 1; i < name.Length; i++) + _builder.Append(name[i]); + return _builder.ToString(); } } diff --git a/src/JsonMergePatch.SourceGenerator/GeneratedTypeFilter.cs b/src/JsonMergePatch.SourceGenerator/GeneratedTypeFilter.cs index 662b827..c195d42 100644 --- a/src/JsonMergePatch.SourceGenerator/GeneratedTypeFilter.cs +++ b/src/JsonMergePatch.SourceGenerator/GeneratedTypeFilter.cs @@ -1,61 +1,58 @@ -using System.Linq; -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; -namespace LaDeak.JsonMergePatch.SourceGenerator -{ - public static class GeneratedTypeFilter - { - public static SymbolDisplayFormat SymbolFormat = new SymbolDisplayFormat(SymbolDisplayGlobalNamespaceStyle.Omitted, SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, SymbolDisplayGenericsOptions.IncludeTypeParameters, SymbolDisplayMemberOptions.None, SymbolDisplayDelegateStyle.NameOnly, SymbolDisplayExtensionMethodStyle.Default, SymbolDisplayParameterOptions.IncludeType, SymbolDisplayPropertyStyle.NameOnly, SymbolDisplayLocalOptions.None, SymbolDisplayKindOptions.None, SymbolDisplayMiscellaneousOptions.None); +namespace LaDeak.JsonMergePatch.SourceGenerator; - public static bool IsGeneratableType(ITypeSymbol typeInfo) - { - bool generic = false; - if (typeInfo is INamedTypeSymbol namedTypeInfo) - generic = namedTypeInfo.IsGenericType; - - // Check for System types that have SpecialType.None. - string typeName = typeInfo.ToDisplayString(); - bool isNonSpecialSystemType = typeName == "System.Guid" || typeName == "System.DateOnly" || typeName == "System.TimeOnly"; - - return typeInfo.SpecialType == SpecialType.None - && !isNonSpecialSystemType - && !typeInfo.IsAnonymousType - && !typeInfo.IsAbstract - && !generic && !typeInfo.IsStatic - && typeInfo.TypeKind != TypeKind.Enum; - } +public static class GeneratedTypeFilter +{ + public static SymbolDisplayFormat SymbolFormat = new SymbolDisplayFormat(SymbolDisplayGlobalNamespaceStyle.Omitted, SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, SymbolDisplayGenericsOptions.IncludeTypeParameters, SymbolDisplayMemberOptions.None, SymbolDisplayDelegateStyle.NameOnly, SymbolDisplayExtensionMethodStyle.Default, SymbolDisplayParameterOptions.IncludeType, SymbolDisplayPropertyStyle.NameOnly, SymbolDisplayLocalOptions.None, SymbolDisplayKindOptions.None, SymbolDisplayMiscellaneousOptions.None); + public static bool IsGeneratableType(ITypeSymbol typeInfo) + { + bool generic = false; + if (typeInfo is INamedTypeSymbol namedTypeInfo) + generic = namedTypeInfo.IsGenericType; + + // Check for System types that have SpecialType.None. + string typeName = typeInfo.ToDisplayString(); + bool isNonSpecialSystemType = typeName == "System.Guid" || typeName == "System.DateOnly" || typeName == "System.TimeOnly"; + + return typeInfo.SpecialType == SpecialType.None + && !isNonSpecialSystemType + && !typeInfo.IsAnonymousType + && !typeInfo.IsAbstract + && !generic && !typeInfo.IsStatic + && typeInfo.TypeKind != TypeKind.Enum; + } - public static bool TryGetGeneratableType(ITypeSymbol typeInfo, out ITypeSymbol generatableType) + public static bool TryGetGeneratableType(ITypeSymbol typeInfo, out ITypeSymbol generatableType) + { + if (typeInfo is INamedTypeSymbol namedTypeInfo) { - if (typeInfo is INamedTypeSymbol namedTypeInfo) + if (namedTypeInfo.IsGenericType && !namedTypeInfo.IsUnboundGenericType) { - if (namedTypeInfo.IsGenericType && !namedTypeInfo.IsUnboundGenericType) + ITypeSymbol? genericTypeArgument = null; + if (namedTypeInfo.TypeArguments.Length == 1 && namedTypeInfo.SpecialType != SpecialType.None) + genericTypeArgument = namedTypeInfo.TypeArguments.First(); + else if (namedTypeInfo.TypeArguments.Length == 2 && namedTypeInfo.Name == "Dictionary" && namedTypeInfo.ContainingNamespace.ToDisplayString() == "System.Collections.Generic") { - ITypeSymbol? genericTypeArgument = null; - if (namedTypeInfo.TypeArguments.Length == 1 && namedTypeInfo.SpecialType != SpecialType.None) - genericTypeArgument = namedTypeInfo.TypeArguments.First(); - else if (namedTypeInfo.TypeArguments.Length == 2 && namedTypeInfo.Name == "Dictionary" && namedTypeInfo.ContainingNamespace.ToDisplayString() == "System.Collections.Generic") - { - genericTypeArgument = namedTypeInfo.TypeArguments.Last(); - if (genericTypeArgument is INamedTypeSymbol dictionaryValueType && dictionaryValueType.IsGenericType && dictionaryValueType.SpecialType == SpecialType.System_Nullable_T) - genericTypeArgument = dictionaryValueType.TypeArguments.First(); - } - if (genericTypeArgument != null && IsGeneratableType(genericTypeArgument)) - { - generatableType = genericTypeArgument; - return true; - } + genericTypeArgument = namedTypeInfo.TypeArguments.Last(); + if (genericTypeArgument is INamedTypeSymbol dictionaryValueType && dictionaryValueType.IsGenericType && dictionaryValueType.SpecialType == SpecialType.System_Nullable_T) + genericTypeArgument = dictionaryValueType.TypeArguments.First(); + } + if (genericTypeArgument != null && IsGeneratableType(genericTypeArgument)) + { + generatableType = genericTypeArgument; + return true; } } - - generatableType = typeInfo; - return IsGeneratableType(typeInfo); } - public static string SourceTypeName(ITypeSymbol typeInfo) - { - return typeInfo.ToDisplayString(SymbolFormat); - } + generatableType = typeInfo; + return IsGeneratableType(typeInfo); + } + + public static string SourceTypeName(ITypeSymbol typeInfo) + { + return typeInfo.ToDisplayString(SymbolFormat); } } diff --git a/src/JsonMergePatch.SourceGenerator/GeneratedWrapper.cs b/src/JsonMergePatch.SourceGenerator/GeneratedWrapper.cs index 0f67fd1..36e1d1b 100644 --- a/src/JsonMergePatch.SourceGenerator/GeneratedWrapper.cs +++ b/src/JsonMergePatch.SourceGenerator/GeneratedWrapper.cs @@ -1,14 +1,9 @@ -using System.Collections.Generic; -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; -namespace LaDeak.JsonMergePatch.SourceGenerator +namespace LaDeak.JsonMergePatch.SourceGenerator; + +public class GeneratedWrapper { - public class GeneratedWrapper - { - public string? FileName { get; set; } - public string? SourceCode { get; set; } - public string? SourceTypeFullName { get; set; } - public string? TargetTypeFullName { get; set; } - public List? ToProcessTypes { get; set; } - } + public string? FileName { get; set; } + public string? SourceCode { get; set; } } diff --git a/src/JsonMergePatch.SourceGenerator/IEnumerableExtensions.cs b/src/JsonMergePatch.SourceGenerator/IEnumerableExtensions.cs index 5b6354f..94b1c73 100644 --- a/src/JsonMergePatch.SourceGenerator/IEnumerableExtensions.cs +++ b/src/JsonMergePatch.SourceGenerator/IEnumerableExtensions.cs @@ -1,17 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; -namespace LaDeak.JsonMergePatch.SourceGenerator +namespace LaDeak.JsonMergePatch.SourceGenerator; + +public static class IEnumerableExtensions { - public static class IEnumerableExtensions + public static bool AnyOrNull(this IEnumerable source, Func predicate) { - public static bool AnyOrNull(this IEnumerable source, Func predicate) - { - if (source is null || !source.GetEnumerator().MoveNext()) - return true; - return source.Any(predicate); - } + if (source is null || !source.GetEnumerator().MoveNext()) + return true; + return source.Any(predicate); } } diff --git a/src/JsonMergePatch.SourceGenerator/IPatchParametersWalker.cs b/src/JsonMergePatch.SourceGenerator/IPatchParametersWalker.cs index 3caaf4c..af44f1b 100644 --- a/src/JsonMergePatch.SourceGenerator/IPatchParametersWalker.cs +++ b/src/JsonMergePatch.SourceGenerator/IPatchParametersWalker.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; namespace LaDeak.JsonMergePatch.SourceGenerator { diff --git a/src/JsonMergePatch.SourceGenerator/ITypeBuilder.cs b/src/JsonMergePatch.SourceGenerator/ITypeBuilder.cs index 881790b..636a260 100644 --- a/src/JsonMergePatch.SourceGenerator/ITypeBuilder.cs +++ b/src/JsonMergePatch.SourceGenerator/ITypeBuilder.cs @@ -1,9 +1,6 @@ -using Microsoft.CodeAnalysis; +namespace LaDeak.JsonMergePatch.SourceGenerator; -namespace LaDeak.JsonMergePatch.SourceGenerator +public interface ITypeBuilder { - public interface ITypeBuilder - { - GeneratedWrapper BuildWrapperType(ITypeSymbol typeInfo, string sourceTypeName); - } + GeneratedWrapper BuildWrapperType(GeneratorClassInfo typeInfo); } \ No newline at end of file diff --git a/src/JsonMergePatch.SourceGenerator/ITypeBuilderGenerator.cs b/src/JsonMergePatch.SourceGenerator/ITypeBuilderGenerator.cs index 5632ce4..7a3587d 100644 --- a/src/JsonMergePatch.SourceGenerator/ITypeBuilderGenerator.cs +++ b/src/JsonMergePatch.SourceGenerator/ITypeBuilderGenerator.cs @@ -1,9 +1,6 @@ -using System.Collections.Generic; +namespace LaDeak.JsonMergePatch.SourceGenerator; -namespace LaDeak.JsonMergePatch.SourceGenerator +public interface ITypeBuilderGenerator { - public interface ITypeBuilderGenerator - { - IEnumerable Generate(); - } + IEnumerable Generate(); } \ No newline at end of file diff --git a/src/JsonMergePatch.SourceGenerator/JsonMergePatch.SourceGenerator.csproj b/src/JsonMergePatch.SourceGenerator/JsonMergePatch.SourceGenerator.csproj index a3fd4f6..f35de82 100644 --- a/src/JsonMergePatch.SourceGenerator/JsonMergePatch.SourceGenerator.csproj +++ b/src/JsonMergePatch.SourceGenerator/JsonMergePatch.SourceGenerator.csproj @@ -1,15 +1,17 @@ - + netstandard2.0 LaDeak.JsonMergePatch.SourceGenerator enable false + true + true - - + + diff --git a/src/JsonMergePatch.SourceGenerator/JsonMergePatchSourceGenerator.cs b/src/JsonMergePatch.SourceGenerator/JsonMergePatchSourceGenerator.cs index c44d95e..ab29de4 100644 --- a/src/JsonMergePatch.SourceGenerator/JsonMergePatchSourceGenerator.cs +++ b/src/JsonMergePatch.SourceGenerator/JsonMergePatchSourceGenerator.cs @@ -1,31 +1,37 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; namespace LaDeak.JsonMergePatch.SourceGenerator; [Generator] -public class JsonMergePatchSourceGenerator : ISourceGenerator +public class JsonMergePatchSourceGenerator : IIncrementalGenerator { - public void Execute(GeneratorExecutionContext context) => ExecuteImpl(context); - - protected virtual IEnumerable ExecuteImpl(GeneratorExecutionContext context) + public void Initialize(IncrementalGeneratorInitializationContext context) { - var typeBuilder = new MultiTypeBuilder(context.Compilation.SyntaxTrees, context.Compilation, new TypeBuilder(), new PatchParametersWalker()); - var types = typeBuilder.Generate(); - foreach (var generatedType in types) - context.AddSource(generatedType.FileName, SourceText.From(generatedType.SourceCode, Encoding.UTF8)); + var provider = context.SyntaxProvider.ForAttributeWithMetadataName("LaDeak.JsonMergePatch.Abstractions.PatchableAttribute", (x, token) => true, + static (GeneratorAttributeSyntaxContext context, CancellationToken token) => + { + var typeSymbol = context.TargetSymbol as INamedTypeSymbol; + if (!GeneratedTypeFilter.TryGetGeneratableType(typeSymbol, out var _)) + return null; + var assemblyName = context.SemanticModel.Compilation.Assembly.Name; + var sourceTypeName = GeneratedTypeFilter.SourceTypeName(typeSymbol); + return new GeneratorClassInfo(typeSymbol, sourceTypeName, assemblyName); + }).Where(x => x != null); - var tyeRepoGenerator = new TypeRepositoryGenerator(); - var typeRepoClass = tyeRepoGenerator.CreateTypeRepository(types.Select(x => (x.SourceTypeFullName, x.TargetTypeFullName)), context.Compilation.Assembly.Name); - context.AddSource("LaDeakJsonMergePatchTypeRepo", SourceText.From(typeRepoClass, Encoding.UTF8)); - return types; - } + context.RegisterImplementationSourceOutput(provider, (spc, classInfo) => + { + var builder = new TypeBuilder(); + var generatedType = builder.BuildWrapperType(classInfo); + spc.AddSource(generatedType.FileName, SourceText.From(generatedType.SourceCode, Encoding.UTF8)); + }); - public void Initialize(GeneratorInitializationContext context) - { + //context.RegisterImplementationSourceOutput(provider.Collect(), (spc, classInfos) => + //{ + // var tyeRepoGenerator = new TypeRepositoryGenerator(); + // var typeRepoClass = tyeRepoGenerator.CreateTypeRepository(classInfos.Select(x => (x.SourceTypeName, x.FullTypeName)), classInfos.First().AssemblyName); + // spc.AddSource("LaDeakJsonMergePatchTypeRepo", SourceText.From(typeRepoClass, Encoding.UTF8)); + //}); } } - diff --git a/src/JsonMergePatch.SourceGenerator/MultiTypeBuilder.cs b/src/JsonMergePatch.SourceGenerator/MultiTypeBuilder.cs deleted file mode 100644 index 99401a0..0000000 --- a/src/JsonMergePatch.SourceGenerator/MultiTypeBuilder.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.CodeAnalysis; - -namespace LaDeak.JsonMergePatch.SourceGenerator; - -public class MultiTypeBuilder -{ - private readonly IEnumerable _syntaxTrees; - private readonly Compilation _compilation; - private readonly ITypeBuilder _typeBuilder; - private readonly IPatchParametersWalker _walker; - private readonly Stack _typesToWrap; - - public MultiTypeBuilder(IEnumerable syntaxTrees, Compilation compilation, ITypeBuilder typeBuilder, IPatchParametersWalker walker) - { - _typesToWrap = new Stack(); - _syntaxTrees = syntaxTrees ?? throw new ArgumentNullException(nameof(syntaxTrees)); - _compilation = compilation ?? throw new ArgumentNullException(nameof(compilation)); - _typeBuilder = typeBuilder ?? throw new ArgumentNullException(nameof(typeBuilder)); - _walker = walker ?? throw new ArgumentNullException(nameof(walker)); - } - - public IEnumerable Generate() - { - _typesToWrap.Clear(); - var result = new List(); - foreach (SyntaxTree tree in _syntaxTrees) - WalkTree(result, tree); - - return result; - } - - private void WalkTree(List result, SyntaxTree tree) - { - var typesToWrap = _walker.Process(tree.GetRoot(), _compilation.GetSemanticModel(tree)); - PushToWrap(typesToWrap); - while (_typesToWrap.Count > 0) - { - var typeInfo = _typesToWrap.Pop(); - var sourceTypeName = GeneratedTypeFilter.SourceTypeName(typeInfo); - if (!result.Any(x => x.SourceTypeFullName == sourceTypeName) - && GeneratedTypeFilter.TryGetGeneratableType(typeInfo, out var typeInfoToGenerate)) - { - var generatedTypeResult = _typeBuilder.BuildWrapperType(typeInfoToGenerate, sourceTypeName); - PushToWrap(generatedTypeResult.ToProcessTypes); - result.Add(generatedTypeResult); - } - } - } - - private void PushToWrap(IEnumerable typesToWrap) - { - foreach (var typeToWrap in typesToWrap) - _typesToWrap.Push(typeToWrap); - } -} - \ No newline at end of file diff --git a/src/JsonMergePatch.SourceGenerator/PatchParametersWalker.cs b/src/JsonMergePatch.SourceGenerator/PatchParametersWalker.cs index 33cb415..2775b5b 100644 --- a/src/JsonMergePatch.SourceGenerator/PatchParametersWalker.cs +++ b/src/JsonMergePatch.SourceGenerator/PatchParametersWalker.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/JsonMergePatch.SourceGenerator/StringBuilderExtensions.cs b/src/JsonMergePatch.SourceGenerator/StringBuilderExtensions.cs index 54f6402..d4e92fb 100644 --- a/src/JsonMergePatch.SourceGenerator/StringBuilderExtensions.cs +++ b/src/JsonMergePatch.SourceGenerator/StringBuilderExtensions.cs @@ -1,14 +1,13 @@ using System.Text; -namespace LaDeak.JsonMergePatch.SourceGenerator +namespace LaDeak.JsonMergePatch.SourceGenerator; + +public static class StringBuilderExtensions { - public static class StringBuilderExtensions + public static StringBuilder AppendLineWithIdentation(this StringBuilder sb, int identation, string line) { - public static StringBuilder AppendLineWithIdentation(this StringBuilder sb, int identation, string line) - { - sb.Append(' ', identation); - sb.AppendLine(line); - return sb; - } + sb.Append(' ', identation); + sb.AppendLine(line); + return sb; } } diff --git a/src/JsonMergePatch.SourceGenerator/TypeBuilder.cs b/src/JsonMergePatch.SourceGenerator/TypeBuilder.cs index 845d856..7e395f5 100644 --- a/src/JsonMergePatch.SourceGenerator/TypeBuilder.cs +++ b/src/JsonMergePatch.SourceGenerator/TypeBuilder.cs @@ -1,118 +1,68 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Reflection; using LaDeak.JsonMergePatch.SourceGenerator.ApplyPatchBuilders; using Microsoft.CodeAnalysis; namespace LaDeak.JsonMergePatch.SourceGenerator; -public class TypeBuilder : ITypeBuilder +public record PropertyInfo { - public GeneratedWrapper BuildWrapperType(ITypeSymbol typeInfo, string sourceTypeName) + public PropertyInfo(IPropertySymbol symbol) { - var name = NameBuilder.GetName(typeInfo); - var state = InitializeState(typeInfo, name, sourceTypeName); - BuildFile(state); - return new GeneratedWrapper() - { - FileName = $"LaDeakJsonMergePatch{NameBuilder.GetNamespaceExtension(typeInfo)}{name}", - SourceCode = state.Builder.ToString(), - SourceTypeFullName = sourceTypeName, - TargetTypeFullName = NameBuilder.GetFullTypeName(typeInfo), - ToProcessTypes = state.ToProcessTypeSymbols - }; + Name = symbol.Name; + Attributes = ReadAttributes(symbol).ToList(); + IsInitOnly = GetIsInitOnly(symbol); + HasGeneratableType = GeneratedTypeFilter.IsGeneratableType(symbol.Type); + PropertyTypeName = GetPropertyTypeName(symbol); } - private void BuildFile(BuilderState state) => BuildNamespace(state, BuildClass); - - private void BuildClass(BuilderState state) => BuildClassDeclaration(state, s => BuildClassBody(s)); - - private BuilderState InitializeState(ITypeSymbol typeInfo, string name, string sourceTypeName) + private static bool GetIsInitOnly(IPropertySymbol symbol) { - var typeInformation = new TypeInformation() { Name = name, SourceTypeName = sourceTypeName, TypeSymbol = typeInfo }; + return symbol.SetMethod?.OriginalDefinition.IsInitOnly ?? false; + } - var currentType = typeInfo; - while (currentType != null && currentType.Name != "Object") - { - typeInformation.Properties.AddRange(currentType.GetMembers().OfType() - .Where(x => !x.IsReadOnly && !x.IsWriteOnly && !x.IsIndexer && !x.IsStatic && !x.IsAbstract && !x.IsVirtual).Select(x => new PropertyInformation() { Property = x, IsConvertedToNullableType = false })); - currentType = currentType.BaseType; - } + public string Name { get; } - return new BuilderState(typeInformation); - } + public List Attributes { get; } - private void BuildNamespace(BuilderState state, Action? addBody = null) - { - state.AppendLine($"#nullable enable"); - state.AppendLine($"namespace {NameBuilder.GetNamespace(state.TypeInfo.TypeSymbol)}"); - state.AppendLine("{"); - addBody?.Invoke(state.IncrementIdentation()); - state.AppendLine("}"); - state.AppendLine($"#nullable disable"); - } + public bool HasGeneratableType { get; } - private void BuildClassDeclaration(BuilderState state, Action? addBody = null) - { - BuildAttributes(state, state.TypeInfo.TypeSymbol.GetAttributes()); - state.AppendLine($"public class {state.TypeInfo.Name} : LaDeak.JsonMergePatch.Abstractions.Patch<{state.TypeInfo.SourceTypeName}>"); - state.AppendLine("{"); - addBody?.Invoke(state.IncrementIdentation()); - state.AppendLine("}"); - } + public bool IsInitOnly { get; } - private void BuildConstructor(BuilderState state, Action? addBody = null) - { - state.AppendLine($"public {state.TypeInfo.Name}()"); - state.AppendLine("{"); - var innerState = state.IncrementIdentation(); - innerState.AppendLine($"Properties = new bool[{state.TypeInfo.Properties.Count}];"); - addBody?.Invoke(innerState); - state.AppendLine("}"); - } + public string PropertyTypeName { get; } + + public ApplyPatchBuilder? Builder { get; private set; } - private void BuildPropery(BuilderState state, PropertyInformation propertyInfo, int propertyId) + private IEnumerable ReadAttributes(IPropertySymbol symbol) { - state.ToProcessTypeSymbols.Add(propertyInfo.Property.Type); - string fieldName = Casing.PrefixUnderscoreCamelCase(propertyInfo.Property.Name); - string propertyTypeName = GetPropertyTypeName(propertyInfo); - state.AppendLine($"private {propertyTypeName} {fieldName};"); - BuildAttributes(state, propertyInfo.Property.GetAttributes()); - state.AppendLine($"public {propertyTypeName} {propertyInfo.Property.Name}"); - state.AppendLine("{"); - var getterSetter = state.IncrementIdentation(); - getterSetter.AppendLine($"get {{ return {fieldName}; }}"); - getterSetter.AppendLine("set"); - getterSetter.AppendLine("{"); - var setterBody = getterSetter.IncrementIdentation(); - setterBody.AppendLine($"Properties[{propertyId}] = true;"); - setterBody.AppendLine($"{fieldName} = value;"); - getterSetter.AppendLine("}"); - state.AppendLine("}"); + return symbol.GetAttributes().Where(attribute => + attribute.AttributeClass?.ToDisplayString() != "System.Runtime.CompilerServices.NullableContextAttribute" + && attribute.AttributeClass?.ToDisplayString() != "System.Runtime.CompilerServices.NullableAttribute" + && attribute.AttributeClass?.ToDisplayString() != "LaDeak.JsonMergePatch.Abstractions.PatchableAttribute"); } - private string GetPropertyTypeName(PropertyInformation propertyInfo) + private string GetPropertyTypeName(IPropertySymbol symbol) { - var propertyTypeSymbol = propertyInfo.Property.Type; - if (propertyTypeSymbol is INamedTypeSymbol namedType && namedType.IsGenericType) - return CreateGenericTypeWithParameters(propertyInfo); + if (symbol.Type is INamedTypeSymbol namedType && namedType.IsGenericType) + return CreateGenericTypeWithParameters(symbol); - return ConvertToNullableIfRequired(propertyInfo, propertyTypeSymbol); + return ConvertToNullableIfRequired(symbol.Type).Item1; } - private string CreateGenericTypeWithParameters(PropertyInformation propertyInfo) + private string CreateGenericTypeWithParameters(IPropertySymbol propertySymbol) { - if (propertyInfo?.Property?.Type is not INamedTypeSymbol namedType || !namedType.IsGenericType) + if (propertySymbol?.Type is not INamedTypeSymbol namedType || !namedType.IsGenericType) throw new InvalidOperationException("Parameter is not generic type parameter."); - propertyInfo.Builder = new SimpleNonGeneratableBuilder(propertyInfo.Property, false); + Builder = new SimpleNonGeneratableBuilder(Name, false); var firstUnderlyingType = GetPropertyTypeName(namedType.TypeArguments.First()).TypeName; var withoutUnderlyingType = namedType.ToDisplayString(new SymbolDisplayFormat(SymbolDisplayGlobalNamespaceStyle.Omitted, SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, SymbolDisplayGenericsOptions.None, SymbolDisplayMemberOptions.None, SymbolDisplayDelegateStyle.NameOnly, SymbolDisplayExtensionMethodStyle.Default, SymbolDisplayParameterOptions.IncludeType, SymbolDisplayPropertyStyle.NameOnly, SymbolDisplayLocalOptions.IncludeType, SymbolDisplayKindOptions.None, SymbolDisplayMiscellaneousOptions.ExpandNullable)); var genericResult = $"{withoutUnderlyingType}<{firstUnderlyingType}"; + bool isConvertedToNullableType = false; foreach (var underlyingType in namedType.TypeArguments.Skip(1).OfType()) { - string genericTypeParam = ConvertToNullableIfRequired(propertyInfo, underlyingType); + (string genericTypeParam, bool isConvertedToNullable) = ConvertToNullableIfRequired(underlyingType); + isConvertedToNullableType |= isConvertedToNullable; genericResult += $", {genericTypeParam}"; } genericResult += ">"; @@ -121,23 +71,24 @@ private string CreateGenericTypeWithParameters(PropertyInformation propertyInfo) genericResult += "?"; } if (namedType.Name.Contains("Dictionary") && namedType.ContainingNamespace.ToDisplayString() == "System.Collections.Generic") - propertyInfo.Builder = new NonGeneratableDictionaryPatchBuilder(namedType, propertyInfo.Property, propertyInfo.IsConvertedToNullableType); + Builder = new NonGeneratableDictionaryPatchBuilder(namedType, Name, HasGeneratableType, isConvertedToNullableType); if (namedType.Name.Contains("List") && namedType.ContainingNamespace.ToDisplayString() == "System.Collections.Generic" - && !IsInitOnlyProperty(propertyInfo.Property) + && !GetIsInitOnly(propertySymbol) && GeneratedTypeFilter.IsGeneratableType(namedType.TypeArguments.First())) - propertyInfo.Builder = new GeneratableListBuilder(namedType, propertyInfo.Property); + Builder = new GeneratableListBuilder(namedType, Name); return genericResult; } - private string ConvertToNullableIfRequired(PropertyInformation propertyInfo, ITypeSymbol typeSymbol) + private (string, bool) ConvertToNullableIfRequired(ITypeSymbol typeSymbol) { (bool isGeneratable, string genericTypeParam) = GetPropertyTypeName(typeSymbol); + bool isConvertedToNullableType = false; if (typeSymbol.IsValueType && typeSymbol.SpecialType != SpecialType.System_Nullable_T && typeSymbol.NullableAnnotation != NullableAnnotation.Annotated) { - propertyInfo.IsConvertedToNullableType = true; + isConvertedToNullableType = true; genericTypeParam = $"System.Nullable<{genericTypeParam}>"; } if (!typeSymbol.IsValueType && typeSymbol.SpecialType != SpecialType.System_Nullable_T) @@ -146,11 +97,11 @@ private string ConvertToNullableIfRequired(PropertyInformation propertyInfo, ITy } if (isGeneratable) - propertyInfo.Builder = new SimpleGeneratableBuilder(propertyInfo.Property); + Builder = new SimpleGeneratableBuilder(Name); else - propertyInfo.Builder = new SimpleNonGeneratableBuilder(propertyInfo.Property, propertyInfo.IsConvertedToNullableType); + Builder = new SimpleNonGeneratableBuilder(Name, isConvertedToNullableType); - return genericTypeParam; + return (genericTypeParam, isConvertedToNullableType); } private (bool IsGeneratedType, string TypeName) GetPropertyTypeName(ITypeSymbol propertyTypeSymbol) @@ -161,6 +112,150 @@ private string ConvertToNullableIfRequired(PropertyInformation propertyInfo, ITy } return (false, propertyTypeSymbol.ToDisplayString(GeneratedTypeFilter.SymbolFormat)); } +} + +public record GeneratorClassInfo +{ + public static GeneratorClassInfo Default => new GeneratorClassInfo(); + + private GeneratorClassInfo() + { + Name = string.Empty; + FullTypeName = string.Empty; + NamespaceExtension = string.Empty; + SourceTypeName = string.Empty; + Namespace = string.Empty; + AssemblyName = string.Empty; + Properties = []; + Attributes = []; + } + + public GeneratorClassInfo(ITypeSymbol typeSymbol, string sourceTypeName, string assemblyName = "") + { + Name = NameBuilder.GetName(typeSymbol); + FullTypeName = NameBuilder.GetFullTypeName(typeSymbol); + NamespaceExtension = NameBuilder.GetNamespaceExtension(typeSymbol); + HasDefaultCtor = HasDefaultConstructor(typeSymbol); + SourceTypeName = sourceTypeName; + AssemblyName = assemblyName; + Properties = ReadProperties(typeSymbol); + Attributes = ReadAttributes(typeSymbol).ToList(); + Namespace = NameBuilder.GetNamespace(typeSymbol); + CtorParameterLength = typeSymbol.GetMembers() + .OfType() + .Where(x => x.MethodKind == MethodKind.Constructor) + .OrderByDescending(x => x.Parameters.Length).FirstOrDefault()?.Parameters.Count() ?? 0; + } + + public string Name { get; } + + public string FullTypeName { get; } + + public string NamespaceExtension { get; } + + public string Namespace { get; } + + public bool HasDefaultCtor { get; } + + public string SourceTypeName { get; } + public string AssemblyName { get; } + public int CtorParameterLength { get; } + + public List Properties { get; } + + public List Attributes { get; } + + private bool HasDefaultConstructor(ITypeSymbol typeSymbol) => + typeSymbol.GetMembers().OfType().Where(x => x.MethodKind == MethodKind.Constructor).AnyOrNull(x => x.Parameters.IsEmpty); + + private List ReadProperties(ITypeSymbol typeSymbol) + { + var properties = new List(); + var currentType = typeSymbol; + while (currentType != null && currentType.Name != "Object") + { + properties.AddRange(currentType.GetMembers().OfType() + .Where(x => !x.IsReadOnly && !x.IsWriteOnly && !x.IsIndexer && !x.IsStatic && !x.IsAbstract && !x.IsVirtual) + .Select(x => new PropertyInfo(x))); + currentType = currentType.BaseType; + } + return properties; + } + + private IEnumerable ReadAttributes(ITypeSymbol typeSymbol) + { + return typeSymbol.GetAttributes().Where(attribute => + attribute.AttributeClass?.ToDisplayString() != "System.Runtime.CompilerServices.NullableContextAttribute" + && attribute.AttributeClass?.ToDisplayString() != "System.Runtime.CompilerServices.NullableAttribute" + && attribute.AttributeClass?.ToDisplayString() != "LaDeak.JsonMergePatch.Abstractions.PatchableAttribute"); + } +} + +public class TypeBuilder : ITypeBuilder +{ + public GeneratedWrapper BuildWrapperType(GeneratorClassInfo typeInfo) + { + var name = typeInfo.Name; + var state = new BuilderState(typeInfo); + BuildFile(state); + return new GeneratedWrapper() + { + FileName = $"LaDeakJsonMergePatch{typeInfo.NamespaceExtension}{name}", + SourceCode = state.Builder.ToString(), + }; + } + + private void BuildFile(BuilderState state) => BuildNamespace(state, BuildClass); + + private void BuildClass(BuilderState state) => BuildClassDeclaration(state, s => BuildClassBody(s)); + + private void BuildNamespace(BuilderState state, Action? addBody = null) + { + state.AppendLine($"#nullable enable"); + state.AppendLine($"namespace {state.TypeInfo.Namespace}"); + state.AppendLine("{"); + addBody?.Invoke(state.IncrementIdentation()); + state.AppendLine("}"); + state.AppendLine($"#nullable disable"); + } + + private void BuildClassDeclaration(BuilderState state, Action? addBody = null) + { + BuildAttributes(state, state.TypeInfo.Attributes); + state.AppendLine($"public class {state.TypeInfo.Name} : LaDeak.JsonMergePatch.Abstractions.Patch<{state.TypeInfo.SourceTypeName}>"); + state.AppendLine("{"); + addBody?.Invoke(state.IncrementIdentation()); + state.AppendLine("}"); + } + + private void BuildConstructor(BuilderState state, Action? addBody = null) + { + state.AppendLine($"public {state.TypeInfo.Name}()"); + state.AppendLine("{"); + var innerState = state.IncrementIdentation(); + innerState.AppendLine($"Properties = new bool[{state.TypeInfo.Properties.Count}];"); + addBody?.Invoke(innerState); + state.AppendLine("}"); + } + + private void BuildPropery(BuilderState state, PropertyInfo propertyInfo, int propertyId) + { + string fieldName = Casing.PrefixUnderscoreCamelCase(propertyInfo.Name); + string propertyTypeName = propertyInfo.PropertyTypeName; + state.AppendLine($"private {propertyTypeName} {fieldName};"); + BuildAttributes(state, propertyInfo.Attributes); + state.AppendLine($"public {propertyTypeName} {propertyInfo.Name}"); + state.AppendLine("{"); + var getterSetter = state.IncrementIdentation(); + getterSetter.AppendLine($"get {{ return {fieldName}; }}"); + getterSetter.AppendLine("set"); + getterSetter.AppendLine("{"); + var setterBody = getterSetter.IncrementIdentation(); + setterBody.AppendLine($"Properties[{propertyId}] = true;"); + setterBody.AppendLine($"{fieldName} = value;"); + getterSetter.AppendLine("}"); + state.AppendLine("}"); + } private void BuildAttributes(BuilderState state, IEnumerable attributes) { @@ -205,7 +300,7 @@ private void BuildApplyPath(BuilderState state) private void SetInitOnlyProperties(BuilderState state) { - if (!state.TypeInfo.Properties.Any(x => IsInitOnlyProperty(x.Property))) + if (!state.TypeInfo.Properties.Any(x => x.IsInitOnly)) return; CallConstructIfEmpty(state, "var tmp =", leaveOpen: true); state.AppendLine("{"); @@ -213,14 +308,14 @@ private void SetInitOnlyProperties(BuilderState state) for (int i = 0; i < state.TypeInfo.Properties.Count; i++) { var currentProperty = state.TypeInfo.Properties[i]; - if (IsInitOnlyProperty(currentProperty.Property)) + if (currentProperty.IsInitOnly) { currentProperty.Builder?.BuildInitOnly(state, i); } else { // Copy old property values onto the new object - initializerState.AppendLine($"{currentProperty.Property.Name} = input.{currentProperty.Property.Name},"); + initializerState.AppendLine($"{currentProperty.Name} = input.{currentProperty.Name},"); } } state.AppendLine("};"); @@ -232,7 +327,7 @@ private void SetReadWriteProperties(BuilderState state) for (int i = 0; i < state.TypeInfo.Properties.Count; i++) { var currentProperty = state.TypeInfo.Properties[i]; - if (!IsInitOnlyProperty(currentProperty.Property)) + if (!currentProperty.IsInitOnly) currentProperty.Builder?.BuildInstantiation(state, i); } } @@ -246,25 +341,12 @@ private static void BuildApplyPatchEnumerations(BuilderState state) } } - private bool IsInitOnlyProperty(IPropertySymbol propertySymbol) - { - return propertySymbol.SetMethod?.OriginalDefinition.IsInitOnly ?? false; - } - - private bool HasDefaultConstructor(ITypeSymbol typeSymbol) - { - return typeSymbol.GetMembers().OfType().Where(x => x.MethodKind == MethodKind.Constructor).AnyOrNull(x => x.Parameters.IsEmpty); - } - private void CallConstructIfEmpty(BuilderState state, string toInitialize, bool leaveOpen) { IEnumerable parameters = Enumerable.Empty(); - if (!HasDefaultConstructor(state.TypeInfo.TypeSymbol)) + if (!state.TypeInfo.HasDefaultCtor) { - var typeSymbol = state.TypeInfo.TypeSymbol; - var ctor = typeSymbol.GetMembers().OfType().Where(x => x.MethodKind == MethodKind.Constructor).OrderByDescending(x => x.Parameters.Length).First(); - var properties = state.TypeInfo.Properties.Select(x => x.Property).ToList(); - parameters = Enumerable.Repeat("default", ctor.Parameters.Length); + parameters = Enumerable.Repeat("default", state.TypeInfo.CtorParameterLength); } var ending = leaveOpen ? "" : ";"; state.AppendLine($"{toInitialize} new {state.TypeInfo.SourceTypeName}({string.Join(", ", parameters)}){ending}"); diff --git a/src/JsonMergePatch.SourceGenerator/TypeInformation.cs b/src/JsonMergePatch.SourceGenerator/TypeInformation.cs deleted file mode 100644 index 951fe14..0000000 --- a/src/JsonMergePatch.SourceGenerator/TypeInformation.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Generic; -using LaDeak.JsonMergePatch.SourceGenerator.ApplyPatchBuilders; -using Microsoft.CodeAnalysis; - -namespace LaDeak.JsonMergePatch.SourceGenerator; - -public class TypeInformation -{ - public string? Name { get; set; } - public string? SourceTypeName { get; set; } - public List Properties { get; } = new List(); - public ITypeSymbol? TypeSymbol { get; set; } -} - -public class PropertyInformation -{ - public IPropertySymbol? Property { get; set; } - public bool IsConvertedToNullableType { get; set; } - public ApplyPatchBuilder? Builder { get; set; } -} diff --git a/src/JsonMergePatch.SourceGenerator/TypeRepositoryGenerator.cs b/src/JsonMergePatch.SourceGenerator/TypeRepositoryGenerator.cs index 07966d2..7e6bd96 100644 --- a/src/JsonMergePatch.SourceGenerator/TypeRepositoryGenerator.cs +++ b/src/JsonMergePatch.SourceGenerator/TypeRepositoryGenerator.cs @@ -1,16 +1,14 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Text; -namespace LaDeak.JsonMergePatch.SourceGenerator +namespace LaDeak.JsonMergePatch.SourceGenerator; + +public class TypeRepositoryGenerator { - public class TypeRepositoryGenerator + public string CreateTypeRepository(IEnumerable<(string, string)> typeRegistrations, string assemblyName) { - public string CreateTypeRepository(IEnumerable<(string, string)> typeRegistrations, string assemblyName) - { - StringBuilder sb = new StringBuilder(@$" + StringBuilder sb = new StringBuilder(@$" namespace {NameBuilder.GetNamespace(assemblyName)}"); - sb.Append(@" + sb.Append(@" { public class TypeRepository : LaDeak.JsonMergePatch.Abstractions.ITypeRepository { @@ -19,10 +17,10 @@ public class TypeRepository : LaDeak.JsonMergePatch.Abstractions.ITypeRepository private TypeRepository() { "); - foreach ((var originalType, var generatedType) in typeRegistrations ?? Enumerable.Empty<(string, string)>()) - sb.AppendLine($" Add<{originalType}, {generatedType}>();"); + foreach ((var originalType, var generatedType) in typeRegistrations ?? Enumerable.Empty<(string, string)>()) + sb.AppendLine($" Add<{originalType}, {generatedType}>();"); - sb.Append(@" + sb.Append(@" } public static LaDeak.JsonMergePatch.Abstractions.ITypeRepository Instance { get; } = new TypeRepository(); @@ -46,8 +44,7 @@ public bool TryGet(System.Type source, [System.Diagnostics.CodeAnalysis.NotNullW public System.Collections.Generic.IEnumerable> GetAll() => _repository; } }"); - return sb.ToString(); - } + return sb.ToString(); } } diff --git a/test/JsonMergePatch.Http.Tests/HttpContentExtensionsTests.cs b/test/JsonMergePatch.Http.Tests/HttpContentExtensionsTests.cs index 7a20006..52bed45 100644 --- a/test/JsonMergePatch.Http.Tests/HttpContentExtensionsTests.cs +++ b/test/JsonMergePatch.Http.Tests/HttpContentExtensionsTests.cs @@ -1,9 +1,6 @@ -using System; -using System.Net.Http; -using System.Net.Http.Headers; +using System.Net.Http.Headers; using System.Text; using System.Text.Json; -using System.Threading.Tasks; using LaDeak.JsonMergePatch.Abstractions; using NSubstitute; using Xunit; @@ -16,14 +13,14 @@ public class HttpContentExtensionsTests public async Task NullContent_ReadJsonPatchAsync_ThrowsArgumentNullException() { HttpContent content = null; - await Assert.ThrowsAsync(() => content.ReadJsonPatchAsync()).ConfigureAwait(false); + await Assert.ThrowsAsync(() => content.ReadJsonPatchAsync()); } [Fact] public async Task NullRepository_ReadJsonPatchAsync_ThrowsArgumentNullException() { var content = Substitute.For(); - await Assert.ThrowsAsync(() => content.ReadJsonPatchAsync(typeRepository: null)).ConfigureAwait(false); + await Assert.ThrowsAsync(() => content.ReadJsonPatchAsync(typeRepository: null)); } [Fact] @@ -32,7 +29,7 @@ public async Task NoRegistrationInRepository_ReadJsonPatchAsync_ThrowsArgumentEx var content = Substitute.For(); var typeRepository = Substitute.For(); typeRepository.TryGet(typeof(TestDto), out Arg.Any()).Returns(false); - await Assert.ThrowsAsync(() => content.ReadJsonPatchAsync(typeRepository)).ConfigureAwait(false); + await Assert.ThrowsAsync(() => content.ReadJsonPatchAsync(typeRepository)); } [Fact] @@ -42,7 +39,7 @@ public async Task NonMergePatchJsonContentType_ReadJsonPatchAsync_ReturnsNull() content.Headers.ContentType = new MediaTypeHeaderValue("application/not-merge-patch+json"); var typeRepository = Substitute.For(); typeRepository.TryGet(typeof(TestDto), out Arg.Any()).Returns(callInfo => { callInfo[1] = typeof(TestDtoWrapped); return true; }); - var result = await content.ReadJsonPatchAsync(typeRepository).ConfigureAwait(false); + var result = await content.ReadJsonPatchAsync(typeRepository); Assert.Null(result); } @@ -53,7 +50,7 @@ public async Task InvalidEncoding_ReadJsonPatchAsync_ThrowsInvalidOperationExcep content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/merge-patch+json; charset=Invalid"); var typeRepository = Substitute.For(); typeRepository.TryGet(typeof(TestDto), out Arg.Any()).Returns(callInfo => { callInfo[1] = typeof(TestDtoWrapped); return true; }); - await Assert.ThrowsAsync(() => content.ReadJsonPatchAsync(typeRepository)).ConfigureAwait(false); + await Assert.ThrowsAsync(() => content.ReadJsonPatchAsync(typeRepository)); } [Fact] @@ -63,7 +60,7 @@ public async Task EmptyEncoding_ReadJsonPatchAsync_UsesUtf8Encoding() content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/merge-patch+json"); var typeRepository = Substitute.For(); typeRepository.TryGet(typeof(TestDto), out Arg.Any()).Returns(callInfo => { callInfo[1] = typeof(TestDtoWrapped); return true; }); - var result = await content.ReadJsonPatchAsync(typeRepository).ConfigureAwait(false); + var result = await content.ReadJsonPatchAsync(typeRepository); Assert.NotNull(result); Assert.Equal(1, result.ApplyOnDefault().Prop1); } @@ -75,7 +72,7 @@ public async Task Utf32Encoding_ReadJsonPatchAsync_UsesUtf32Encoding() content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/merge-patch+json; charset=utf-32"); var typeRepository = Substitute.For(); typeRepository.TryGet(typeof(TestDto), out Arg.Any()).Returns(callInfo => { callInfo[1] = typeof(TestDtoWrapped); return true; }); - var result = await content.ReadJsonPatchAsync(typeRepository).ConfigureAwait(false); + var result = await content.ReadJsonPatchAsync(typeRepository); Assert.NotNull(result); Assert.Equal(1, result.ApplyOnDefault().Prop1); } @@ -87,7 +84,7 @@ public async Task DoubleQuotesInCharset_ReadJsonPatchAsync_IgnoredWhenParsingEnc content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/merge-patch+json; charset=\"utf-32\""); var typeRepository = Substitute.For(); typeRepository.TryGet(typeof(TestDto), out Arg.Any()).Returns(callInfo => { callInfo[1] = typeof(TestDtoWrapped); return true; }); - var result = await content.ReadJsonPatchAsync(typeRepository).ConfigureAwait(false); + var result = await content.ReadJsonPatchAsync(typeRepository); Assert.NotNull(result); Assert.Equal(1, result.ApplyOnDefault().Prop1); } diff --git a/test/JsonMergePatch.Http.Tests/JsonMergePatch.Http.Tests.csproj b/test/JsonMergePatch.Http.Tests/JsonMergePatch.Http.Tests.csproj index 4978aab..ce35278 100644 --- a/test/JsonMergePatch.Http.Tests/JsonMergePatch.Http.Tests.csproj +++ b/test/JsonMergePatch.Http.Tests/JsonMergePatch.Http.Tests.csproj @@ -4,13 +4,14 @@ net8.0 false LaDeak.JsonMergePatch.Http.Tests + true - + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/test/JsonMergePatch.Http.Tests/TestDto.cs b/test/JsonMergePatch.Http.Tests/TestDto.cs index 8744788..379173f 100644 --- a/test/JsonMergePatch.Http.Tests/TestDto.cs +++ b/test/JsonMergePatch.Http.Tests/TestDto.cs @@ -1,7 +1,6 @@ -namespace LaDeak.JsonMergePatch.Http.Tests +namespace LaDeak.JsonMergePatch.Http.Tests; + +public class TestDto { - public class TestDto - { - public int Prop1 { get; set; } - } + public int Prop1 { get; set; } } diff --git a/test/JsonMergePatch.Http.Tests/TestDtoWrapped.cs b/test/JsonMergePatch.Http.Tests/TestDtoWrapped.cs index b2d37fe..11f6739 100644 --- a/test/JsonMergePatch.Http.Tests/TestDtoWrapped.cs +++ b/test/JsonMergePatch.Http.Tests/TestDtoWrapped.cs @@ -1,23 +1,22 @@ using LaDeak.JsonMergePatch.Abstractions; -namespace LaDeak.JsonMergePatch.Http.Tests +namespace LaDeak.JsonMergePatch.Http.Tests; + +public class TestDtoWrapped : Patch { - public class TestDtoWrapped : Patch + public TestDtoWrapped() { - public TestDtoWrapped() - { - Properties = new bool[1]; - } + Properties = new bool[1]; + } - private int? _prop1; - public int? Prop1 { get => _prop1; set { Properties[0] = true; _prop1 = value; } } + private int? _prop1; + public int? Prop1 { get => _prop1; set { Properties[0] = true; _prop1 = value; } } - public override TestDto ApplyPatch(TestDto input) - { - input ??= new(); - if (Properties[0]) - input.Prop1 = Prop1.HasValue ? Prop1.Value : default; - return input; - } + public override TestDto ApplyPatch(TestDto input) + { + input ??= new(); + if (Properties[0]) + input.Prop1 = Prop1.HasValue ? Prop1.Value : default; + return input; } } diff --git a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/BasicSerializationTests.cs b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/BasicSerializationTests.cs index dbe3bdc..c5f7367 100644 --- a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/BasicSerializationTests.cs +++ b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/BasicSerializationTests.cs @@ -119,12 +119,16 @@ public BasicSerializationData() private readonly string _classesWithReadWriteProperties = @" namespace TestCode { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class SubDto { public System.Int32 NumberProp { get; set; } public System.DateTime? NullableDateTimeProperty { get; set; } [System.Text.Json.Serialization.JsonPropertyNameAttribute(""camelCaseProperty"")] public System.Double CamelCaseProperty { get; set; } } + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class ParentDto { public System.String ParentStringProperty { get; set; } public SubDto OtherDto { get; set; } public System.Collections.Generic.IEnumerable Values { get; set; } } + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class SomeBase { public System.Int32 NumberPropBase { get; set; } } + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class SomeDerived : SomeBase { public System.String SomeDerivedProp { get; set; } } public class Program @@ -143,8 +147,10 @@ public void SomeMethod2(LaDeak.JsonMergePatch.Abstractions.Patch da private readonly string _classesWithInitOnlyProperties = @" namespace TestCode { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class SubDto { public System.Int32 NumberProp { get; init; } public System.DateTime? NullableDateTimeProperty { get; init; } [System.Text.Json.Serialization.JsonPropertyNameAttribute(""camelCaseProperty"")] public System.Double CamelCaseProperty { get; init; } } + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class ParentDto { public System.String ParentStringProperty { get; init; } public SubDto OtherDto { get; init; } public System.Collections.Generic.IEnumerable Values { get; init; } } public class Program @@ -159,8 +165,10 @@ public void SomeMethod(LaDeak.JsonMergePatch.Abstractions.Patch data) private readonly string _recordsWithInitOnlyProperties = @" namespace TestCode { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public record SubDto { public System.Int32 NumberProp { get; init; } public System.DateTime? NullableDateTimeProperty { get; init; } [System.Text.Json.Serialization.JsonPropertyNameAttribute(""camelCaseProperty"")] public System.Double CamelCaseProperty { get; init; } } + [LaDeak.JsonMergePatch.Abstractions.Patchable] public record ParentDto { public System.String ParentStringProperty { get; init; } public SubDto OtherDto { get; init; } public System.Collections.Generic.IEnumerable Values { get; init; } } public class Program @@ -175,8 +183,10 @@ public void SomeMethod(LaDeak.JsonMergePatch.Abstractions.Patch data) private readonly string _vanillaRecordTypesProperties = @" namespace TestCode { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public record SubDto(System.Int32 NumberProp, System.DateTime? NullableDateTimeProperty, System.Double CamelCaseProperty); + [LaDeak.JsonMergePatch.Abstractions.Patchable] public record ParentDto(System.String ParentStringProperty, SubDto OtherDto, System.Collections.Generic.IEnumerable Values); public class Program diff --git a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/BuilderStateTests.cs b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/BuilderStateTests.cs index c8cc7eb..5368e9b 100644 --- a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/BuilderStateTests.cs +++ b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/BuilderStateTests.cs @@ -1,83 +1,70 @@ using System; using Xunit; -namespace LaDeak.JsonMergePatch.SourceGenerator.Tests +namespace LaDeak.JsonMergePatch.SourceGenerator.Tests; + +public class BuilderStateTests { - public class BuilderStateTests + [Fact] + public void Increment_Increases_Identation() { - [Fact] - public void NewBuilder_Initialized() - { - var typeInfo = new TypeInformation(); - var sut = new BuilderState(typeInfo); - Assert.Same(typeInfo, sut.TypeInfo); - Assert.Equal(0, sut.Identation); - Assert.Empty(sut.ToProcessTypeSymbols); - Assert.NotNull(sut.Builder); - } - - [Fact] - public void Increment_Increases_Identation() - { - var sut = new BuilderState(new TypeInformation()); - var state = sut.IncrementIdentation(); - Assert.Equal(0, sut.Identation); - Assert.Equal(4, state.Identation); - } - - [Fact] - public void Increment_DoesNotChange_TypeInformation_Builder_ToProcessTypeSymbols() - { - var sut = new BuilderState(new TypeInformation()); - var state = sut.IncrementIdentation(); - Assert.Same(sut.Builder, state.Builder); - Assert.Same(sut.TypeInfo, state.TypeInfo); - Assert.Same(sut.ToProcessTypeSymbols, state.ToProcessTypeSymbols); - } + var sut = new BuilderState(GeneratorClassInfo.Default); + var state = sut.IncrementIdentation(); + Assert.Equal(0, sut.Identation); + Assert.Equal(4, state.Identation); + } - [Fact] - public void AppendLine_Adds_EmptyLine() - { - var sut = new BuilderState(new TypeInformation()); - sut.AppendLine(); - Assert.Equal(Environment.NewLine, sut.Builder.ToString()); - } + [Fact] + public void Increment_DoesNotChange_GeneratorClassInfo_Builder_ToProcessTypeSymbols() + { + var sut = new BuilderState(GeneratorClassInfo.Default); + var state = sut.IncrementIdentation(); + Assert.Same(sut.Builder, state.Builder); + Assert.Same(sut.TypeInfo, state.TypeInfo); + } - [Fact] - public void AppendLineWithText_Adds_Text() - { - var sut = new BuilderState(new TypeInformation()); - sut.AppendLine("hello"); - Assert.Equal($"hello{Environment.NewLine}", sut.Builder.ToString()); - } + [Fact] + public void AppendLine_Adds_EmptyLine() + { + var sut = new BuilderState(GeneratorClassInfo.Default); + sut.AppendLine(); + Assert.Equal(Environment.NewLine, sut.Builder.ToString()); + } - [Fact] - public void AppendLineWithText_Adds_IdentationSpacesAndText() - { - var baseIdentation = new BuilderState(new TypeInformation()); - var sut = baseIdentation.IncrementIdentation(); - sut.AppendLine("world"); - Assert.Equal($" world{Environment.NewLine}", sut.Builder.ToString()); - } + [Fact] + public void AppendLineWithText_Adds_Text() + { + var sut = new BuilderState(GeneratorClassInfo.Default); + sut.AppendLine("hello"); + Assert.Equal($"hello{Environment.NewLine}", sut.Builder.ToString()); + } - [Fact] - public void Builder_AppendsLine_BasedOnItsIdentation() - { - var baseIdentation = new BuilderState(new TypeInformation()); - baseIdentation.AppendLine("hello"); - var sut = baseIdentation.IncrementIdentation(); - sut.AppendLine("world"); - baseIdentation.AppendLine("!"); - Assert.Equal($"hello{Environment.NewLine} world{Environment.NewLine}!{Environment.NewLine}", sut.Builder.ToString()); - } + [Fact] + public void AppendLineWithText_Adds_IdentationSpacesAndText() + { + var baseIdentation = new BuilderState(GeneratorClassInfo.Default); + var sut = baseIdentation.IncrementIdentation(); + sut.AppendLine("world"); + Assert.Equal($" world{Environment.NewLine}", sut.Builder.ToString()); + } - [Fact] - public void DoubleIncrement_Adds_DoubleIdentation() - { - var sut = new BuilderState(new TypeInformation()).IncrementIdentation().IncrementIdentation(); - sut.AppendLine("hello"); - Assert.Equal($" hello{Environment.NewLine}", sut.Builder.ToString()); - } + [Fact] + public void Builder_AppendsLine_BasedOnItsIdentation() + { + var baseIdentation = new BuilderState(GeneratorClassInfo.Default); + baseIdentation.AppendLine("hello"); + var sut = baseIdentation.IncrementIdentation(); + sut.AppendLine("world"); + baseIdentation.AppendLine("!"); + Assert.Equal($"hello{Environment.NewLine} world{Environment.NewLine}!{Environment.NewLine}", sut.Builder.ToString()); + } + [Fact] + public void DoubleIncrement_Adds_DoubleIdentation() + { + var sut = new BuilderState(GeneratorClassInfo.Default).IncrementIdentation().IncrementIdentation(); + sut.AppendLine("hello"); + Assert.Equal($" hello{Environment.NewLine}", sut.Builder.ToString()); } + } diff --git a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/DictionarySerializationTests.cs b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/DictionarySerializationTests.cs index fdc3fce..d1fcf31 100644 --- a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/DictionarySerializationTests.cs +++ b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/DictionarySerializationTests.cs @@ -64,6 +64,7 @@ public class DictionarySerializationData : IEnumerable private const string NullableType = @" namespace TestCode { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class Dto { public System.Collections.Generic.Dictionary Values { get; set; } } public class Program @@ -78,6 +79,7 @@ public void SomeMethod(LaDeak.JsonMergePatch.Abstractions.Patch data) private const string ValueType = @" namespace TestCode { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class Dto { public System.Collections.Generic.Dictionary Values { get; set; } } public class Program @@ -93,6 +95,7 @@ public void SomeMethod(LaDeak.JsonMergePatch.Abstractions.Patch data) private const string ReferenceType = @" namespace TestCode { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class Dto { public System.Collections.Generic.Dictionary Values { get; set; } } public class Program @@ -107,6 +110,7 @@ public void SomeMethod(LaDeak.JsonMergePatch.Abstractions.Patch data) private const string NullableReferenceType = @" namespace TestCode { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class Dto { public System.Collections.Generic.Dictionary Values { get; set; } } public class Program @@ -121,6 +125,7 @@ public void SomeMethod(LaDeak.JsonMergePatch.Abstractions.Patch data) private const string RecordWithDictionaryType = @" namespace TestCode { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public record Dto { public System.Collections.Generic.Dictionary Values { get; set; } } public class Program @@ -135,6 +140,7 @@ public void SomeMethod(LaDeak.JsonMergePatch.Abstractions.Patch data) private const string InitOnlyDictionaryOnRecordType = @" namespace TestCode { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public record Dto { public System.Collections.Generic.Dictionary Values { get; init; } } public class Program @@ -149,6 +155,7 @@ public void SomeMethod(LaDeak.JsonMergePatch.Abstractions.Patch data) private const string InitOnlyDictionaryOnClassType = @" namespace TestCode { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class Dto { public System.Collections.Generic.Dictionary Values { get; init; } } public class Program diff --git a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/GeneratedWrapperTypeTests.cs b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/GeneratedWrapperTypeTests.cs index d2b2a0e..41926ec 100644 --- a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/GeneratedWrapperTypeTests.cs +++ b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/GeneratedWrapperTypeTests.cs @@ -171,8 +171,10 @@ private static (Compilation Input, Compilation Output) CreateInputOutputCompilat Compilation inputCompilation = CreateCompilation(@" namespace TestCode { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class Dto1 { public System.Int32 NumberProp { get; set; } } + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class Dto2 { public System.String Property { get; set; } public Dto1 OtherDto { get; set; } } public class Program2 diff --git a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/IEnumerableExtensionsTests.cs b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/IEnumerableExtensionsTests.cs index af3061a..8fcba6a 100644 --- a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/IEnumerableExtensionsTests.cs +++ b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/IEnumerableExtensionsTests.cs @@ -2,58 +2,57 @@ using System.Linq; using Xunit; -namespace LaDeak.JsonMergePatch.SourceGenerator.Tests +namespace LaDeak.JsonMergePatch.SourceGenerator.Tests; + +public class IEnumerableExtensionsTests { - public class IEnumerableExtensionsTests + [Fact] + public void Null_AnyOrNull_ReturnsTrue() + { + IEnumerable source = null; + Assert.True(source.AnyOrNull(x => x == 0)); + } + + [Fact] + public void Empty_AnyOrNull_ReturnsTrue() + { + IEnumerable source = Enumerable.Empty(); + Assert.True(source.AnyOrNull(x => x == 0)); + } + + [Fact] + public void FalsePredicate_AnyOrNull_ReturnsFalse() { - [Fact] - public void Null_AnyOrNull_ReturnsTrue() - { - IEnumerable source = null; - Assert.True(source.AnyOrNull(x => x == 0)); - } - - [Fact] - public void Empty_AnyOrNull_ReturnsTrue() - { - IEnumerable source = Enumerable.Empty(); - Assert.True(source.AnyOrNull(x => x == 0)); - } - - [Fact] - public void FalsePredicate_AnyOrNull_ReturnsFalse() - { - IEnumerable source = new[] { 1, 2, 3 }; - Assert.False(source.AnyOrNull(x => x == 0)); - } - - [Fact] - public void TruePredicateFirst_AnyOrNull_ReturnsTrue() - { - IEnumerable source = new[] { 0, 1, 2 }; - Assert.True(source.AnyOrNull(x => x == 0)); - } - - [Fact] - public void TruePredicateLast_AnyOrNull_ReturnsTrue() - { - IEnumerable source = new[] { 1, 2, 0 }; - Assert.True(source.AnyOrNull(x => x == 0)); - } - - [Fact] - public void TruePredicateMiddle_AnyOrNull_ReturnsTrue() - { - IEnumerable source = new[] { 1, 0, 2 }; - Assert.True(source.AnyOrNull(x => x == 0)); - } - - [Fact] - public void WithNull_AnyOrNull_ReturnsTrue() - { - IEnumerable source = new[] { null, "hello" }; - Assert.True(source.AnyOrNull(x => x == "hello")); - } + IEnumerable source = new[] { 1, 2, 3 }; + Assert.False(source.AnyOrNull(x => x == 0)); + } + [Fact] + public void TruePredicateFirst_AnyOrNull_ReturnsTrue() + { + IEnumerable source = new[] { 0, 1, 2 }; + Assert.True(source.AnyOrNull(x => x == 0)); } + + [Fact] + public void TruePredicateLast_AnyOrNull_ReturnsTrue() + { + IEnumerable source = new[] { 1, 2, 0 }; + Assert.True(source.AnyOrNull(x => x == 0)); + } + + [Fact] + public void TruePredicateMiddle_AnyOrNull_ReturnsTrue() + { + IEnumerable source = new[] { 1, 0, 2 }; + Assert.True(source.AnyOrNull(x => x == 0)); + } + + [Fact] + public void WithNull_AnyOrNull_ReturnsTrue() + { + IEnumerable source = new[] { null, "hello" }; + Assert.True(source.AnyOrNull(x => x == "hello")); + } + } diff --git a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/JsonMergePatch.SourceGenerator.Tests.csproj b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/JsonMergePatch.SourceGenerator.Tests.csproj index a054728..d0faf91 100644 --- a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/JsonMergePatch.SourceGenerator.Tests.csproj +++ b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/JsonMergePatch.SourceGenerator.Tests.csproj @@ -7,12 +7,12 @@ - - - + + + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/JsonMergePatchSourceGeneratorTests.cs b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/JsonMergePatchSourceGeneratorTests.cs index 4d3cbed..1ee7e77 100644 --- a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/JsonMergePatchSourceGeneratorTests.cs +++ b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/JsonMergePatchSourceGeneratorTests.cs @@ -17,6 +17,7 @@ public void SinglePatchType_ExtensionAndTypeAddedToSource() Compilation inputCompilation = CreateCompilation(@" namespace TestCode1 { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class Dto { public int Property { get; set; } } public class Program { @@ -45,7 +46,9 @@ public void MultiplePatchType_ExtensionAndTypeAddedToSource() Compilation inputCompilation = CreateCompilation(@" namespace TestCode2 { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class Dto0 { public double Property { get; set; } } + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class Dto1 { public Dto0 Property { get; set; } } public class Program @@ -80,6 +83,7 @@ public void DtoWithProperties_WrapperTypeAddedToSource() Compilation inputCompilation = CreateCompilation(@" namespace TestCode3 { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class Dto4 { public System.String Property { get; set; } } public class Program2 @@ -106,6 +110,7 @@ public void RecordType_ExtensionAndTypeAddedToSource() Compilation inputCompilation = CreateCompilation(@" namespace TestCode1 { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public record Dto { public int Property { get; set; } } public class Program { @@ -135,6 +140,7 @@ public void RecordTypeWithInitValueProperty_ExtensionAndTypeAddedToSource() Compilation inputCompilation = CreateCompilation(@" namespace TestCode1 { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public record Dto { public int Property { get; set; } } public class Program { @@ -163,7 +169,9 @@ public void MultiplePatchType_WithInitProperties_ExtensionAndTypeAddedToSource() Compilation inputCompilation = CreateCompilation(@" namespace TestCode2 { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public record Dto0 { public double Property { get; set; } } + [LaDeak.JsonMergePatch.Abstractions.Patchable] public record Dto1 { public Dto0 Property { get; set; } } public class Program @@ -199,6 +207,7 @@ public void RecordTypeWithInitReferenceProperty_ExtensionAndTypeAddedToSource() Compilation inputCompilation = CreateCompilation(@" namespace TestCode1 { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public record Dto { public string Property { get; set; } } public class Program { @@ -228,6 +237,7 @@ public void ClassTypeWithInitOnlyValueProperty_ExtensionAndTypeAddedToSource() Compilation inputCompilation = CreateCompilation(@" namespace TestCode1 { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class Dto { public int Property { get; set; } } public class Program { @@ -256,7 +266,9 @@ public void MultipleClassTypeType_WithInitProperties_ExtensionAndTypeAddedToSour Compilation inputCompilation = CreateCompilation(@" namespace TestCode2 { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class Dto0 { public double Property { get; set; } } + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class Dto1 { public Dto0 Property { get; set; } } public class Program @@ -292,6 +304,7 @@ public void ClassType_WithInitReferenceProperty_ExtensionAndTypeAddedToSource() Compilation inputCompilation = CreateCompilation(@" namespace TestCode1 { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class Dto { public string Property { get; set; } } public class Program { @@ -321,6 +334,7 @@ public void ClassType_InitOnlyWithGetSetProperty_ExtensionAndTypeAddedToSource() Compilation inputCompilation = CreateCompilation(@" namespace TestCode1 { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public class Dto { public string Property { get; set; } public string Property1 { get; set; } } public class Program { @@ -350,6 +364,7 @@ public void DefaultRecordType_ExtensionAndTypeAddedToSource() Compilation inputCompilation = CreateCompilation(@" namespace TestCode1 { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public record Dto(int Property0, string Property1); public class Program { diff --git a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/MultiTypeBuilderTests.cs b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/MultiTypeBuilderTests.cs deleted file mode 100644 index e5ba8c1..0000000 --- a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/MultiTypeBuilderTests.cs +++ /dev/null @@ -1,189 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using NSubstitute; -using Xunit; - -namespace LaDeak.JsonMergePatch.SourceGenerator.Tests -{ - public class MultiTypeBuilderTests - { - private const string SimpleTestCode = @" -namespace TestCode1 -{ - public class Dto { public int Property { get; set; } } - public class Program - { - public void SomeMethod(LaDeak.JsonMergePatch.Abstractions.Patch data) - { - } - } -} -"; - [Fact] - public void MissingDependency_AtConstruction_ThrowsArgumentNullException() - { - Compilation inputCompilation = CreateCompilation(SimpleTestCode); - Assert.Throws(() => new MultiTypeBuilder(null, inputCompilation, Substitute.For(), Substitute.For())); - Assert.Throws(() => new MultiTypeBuilder(new[] { CSharpSyntaxTree.ParseText(SimpleTestCode) }, null, Substitute.For(), Substitute.For())); - Assert.Throws(() => new MultiTypeBuilder(new[] { CSharpSyntaxTree.ParseText(SimpleTestCode) }, inputCompilation, null, Substitute.For())); - Assert.Throws(() => new MultiTypeBuilder(new[] { CSharpSyntaxTree.ParseText(SimpleTestCode) }, inputCompilation, Substitute.For(), null)); - } - - [Fact] - public void WithAllDependencies_AtConstruction_CreatesMultiTypeBuilder() - { - Compilation inputCompilation = CreateCompilation(SimpleTestCode); - var sut = new MultiTypeBuilder(new[] { CSharpSyntaxTree.ParseText(SimpleTestCode) }, inputCompilation, Substitute.For(), Substitute.For()); - } - - [Fact] - public void SimpleTestCode_Generate_ReturnsWrappedDtoType() - { - (Compilation inputCompilation, SyntaxTree tree) = SourceBuilder.Compile(SimpleTestCode); - var walker = Substitute.For(); - walker.Process(Arg.Any(), Arg.Any()).Returns(new[] { Substitute.For() }); - var typeBuilder = Substitute.For(); - typeBuilder.BuildWrapperType(Arg.Any(), Arg.Any()).Returns(new GeneratedWrapper() { ToProcessTypes = new List() }); - var sut = new MultiTypeBuilder(new[] { tree }, inputCompilation, typeBuilder, walker); - - var results = sut.Generate(); - - Assert.Single(results); - } - - [Fact] - public void MultipleTypesFound_Generate_ReturnsWrappedForAllTypes() - { - (Compilation inputCompilation, SyntaxTree tree) = SourceBuilder.Compile(SimpleTestCode); - var walker = Substitute.For(); - walker.Process(Arg.Any(), Arg.Any()).Returns(new[] { Substitute.For(), Substitute.For(), Substitute.For() }); - var typeBuilder = Substitute.For(); - typeBuilder.BuildWrapperType(Arg.Any(), Arg.Any()).Returns(new GeneratedWrapper() { ToProcessTypes = new List() }); - var sut = new MultiTypeBuilder(new[] { tree }, inputCompilation, typeBuilder, walker); - - var results = sut.Generate(); - - Assert.Equal(3, results.Count()); - } - - [Fact] - public void MultipleSyntaxTrees_Generate_ReturnsWrappedForAllTrees() - { - (Compilation inputCompilation, SyntaxTree tree) = SourceBuilder.Compile(SimpleTestCode); - var walker = Substitute.For(); - walker.Process(Arg.Any(), Arg.Any()).Returns(new[] { Substitute.For() }); - var typeBuilder = Substitute.For(); - typeBuilder.BuildWrapperType(Arg.Any(), Arg.Any()).Returns(new GeneratedWrapper() { ToProcessTypes = new List() }); - var sut = new MultiTypeBuilder(new[] { tree, tree, tree }, inputCompilation, typeBuilder, walker); - - var results = sut.Generate(); - - Assert.Equal(3, results.Count()); - } - - [Fact] - public void GeneratedTypeReturnInnerType_Generate_ReturnsWrappedTypeForInner() - { - (Compilation inputCompilation, SyntaxTree tree) = SourceBuilder.Compile(SimpleTestCode); - var walker = Substitute.For(); - walker.Process(Arg.Any(), Arg.Any()).Returns(new[] { Substitute.For() }); - var typeBuilder = Substitute.For(); - typeBuilder.BuildWrapperType(Arg.Any(), Arg.Any()).Returns(new GeneratedWrapper() { ToProcessTypes = new List() { Substitute.For() } }, new GeneratedWrapper() { ToProcessTypes = new List() }); - var sut = new MultiTypeBuilder(new[] { tree }, inputCompilation, typeBuilder, walker); - - var results = sut.Generate(); - - Assert.Equal(2, results.Count()); - } - - [Fact] - public void MultipleInnerType_Generate_ReturnsWrappedMultipleInnerType() - { - (Compilation inputCompilation, SyntaxTree tree) = SourceBuilder.Compile(SimpleTestCode); - var walker = Substitute.For(); - walker.Process(Arg.Any(), Arg.Any()).Returns(new[] { Substitute.For() }); - var typeBuilder = Substitute.For(); - typeBuilder.BuildWrapperType(Arg.Any(), Arg.Any()) - .Returns( - new GeneratedWrapper() { ToProcessTypes = new List() { Substitute.For(), Substitute.For() } }, - new GeneratedWrapper() { ToProcessTypes = new List() }, - new GeneratedWrapper() { ToProcessTypes = new List() }); - var sut = new MultiTypeBuilder(new[] { tree }, inputCompilation, typeBuilder, walker); - - var results = sut.Generate(); - - Assert.Equal(3, results.Count()); - } - - [Fact] - public void NoSyntaxTree_Generate_ReturnsEmptyResults() - { - (Compilation inputCompilation, SyntaxTree tree) = SourceBuilder.Compile(SimpleTestCode); - var walker = Substitute.For(); - var typeBuilder = Substitute.For(); - typeBuilder.BuildWrapperType(Arg.Any(), Arg.Any()).Returns(new GeneratedWrapper() { ToProcessTypes = new List() }); - var sut = new MultiTypeBuilder(Enumerable.Empty(), inputCompilation, typeBuilder, walker); - - var results = sut.Generate(); - - Assert.Empty(results); - walker.DidNotReceive().Process(Arg.Any(), Arg.Any()); - } - - [Fact] - public void WalkerReturnsNoFinding_Generate_ReturnsEmptyResults() - { - (Compilation inputCompilation, SyntaxTree tree) = SourceBuilder.Compile(SimpleTestCode); - var walker = Substitute.For(); - walker.Process(Arg.Any(), Arg.Any()).Returns(Enumerable.Empty()); - var typeBuilder = Substitute.For(); - var sut = new MultiTypeBuilder(new[] { tree }, inputCompilation, typeBuilder, walker); - - var results = sut.Generate(); - - Assert.Empty(results); - typeBuilder.DidNotReceive().BuildWrapperType(Arg.Any(), Arg.Any()); - } - - [Fact] - public void MultipleGenerateCalls_ReturnsEqualResults() - { - (Compilation inputCompilation, SyntaxTree tree) = SourceBuilder.Compile(SimpleTestCode); - var walker = Substitute.For(); - walker.Process(Arg.Any(), Arg.Any()).Returns(new[] { Substitute.For() }); - var typeBuilder = Substitute.For(); - typeBuilder.BuildWrapperType(Arg.Any(), Arg.Any()).Returns(new GeneratedWrapper() { ToProcessTypes = new List() }); - var sut = new MultiTypeBuilder(new[] { tree }, inputCompilation, typeBuilder, walker); - - var results0 = sut.Generate(); - var results1 = sut.Generate(); - - Assert.Equal(results0.Single(), results1.Single()); - } - - [Fact] - public void GeneratedWrappersSource_SameAsReturnedByTypeBuilder() - { - (Compilation inputCompilation, SyntaxTree tree) = SourceBuilder.Compile(SimpleTestCode); - var walker = Substitute.For(); - walker.Process(Arg.Any(), Arg.Any()).Returns(new[] { Substitute.For() }); - var typeBuilder = Substitute.For(); - var typeBuilderResult = new GeneratedWrapper() { ToProcessTypes = new List(), FileName = "file.cs", SourceCode = "namespace TestNameSpace { }", SourceTypeFullName = "Source.FullName", TargetTypeFullName = "Target.FullName" }; - typeBuilder.BuildWrapperType(Arg.Any(), Arg.Any()).Returns(typeBuilderResult); - var sut = new MultiTypeBuilder(new[] { tree }, inputCompilation, typeBuilder, walker); - - var result = sut.Generate().Single(); - - Assert.Equal(typeBuilderResult.FileName, result.FileName); - Assert.Equal(typeBuilderResult.SourceCode, result.SourceCode); - Assert.Equal(typeBuilderResult.SourceTypeFullName, result.SourceTypeFullName); - Assert.Equal(typeBuilderResult.TargetTypeFullName, result.TargetTypeFullName); - } - - private static Compilation CreateCompilation(string source) => SourceBuilder.Compile(source).Compilation; - - } -} diff --git a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/NameBuilderTests.cs b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/NameBuilderTests.cs index 59ec057..27ee3cd 100644 --- a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/NameBuilderTests.cs +++ b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/NameBuilderTests.cs @@ -1,59 +1,53 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; using NSubstitute; using Xunit; -namespace LaDeak.JsonMergePatch.SourceGenerator.Tests +namespace LaDeak.JsonMergePatch.SourceGenerator.Tests; + +public class NameBuilderTests { - public class NameBuilderTests + [Fact] + public void GetName_ReturnsTypeName_WithWrappedSuffix() { - [Fact] - public void GetName_ReturnsTypeName_WithWrappedSuffix() - { - var typeSymbol = Substitute.For(); - typeSymbol.Name.Returns("TestName"); - var result = NameBuilder.GetName(typeSymbol); - Assert.Equal("TestNameWrapped", result); - } + var typeSymbol = Substitute.For(); + typeSymbol.Name.Returns("TestName"); + var result = NameBuilder.GetName(typeSymbol); + Assert.Equal("TestNameWrapped", result); + } - [Fact] - public void GetNamespaceExtension_Returns_SafeExtension() - { - var typeSymbol = Substitute.For(); - typeSymbol.ContainingNamespace.ToDisplayString().Returns("Test.Namespace"); - var result = NameBuilder.GetNamespaceExtension(typeSymbol); - Assert.Equal("SafeTest.Namespace", result); - } + [Fact] + public void GetNamespaceExtension_Returns_SafeExtension() + { + var typeSymbol = Substitute.For(); + typeSymbol.ContainingNamespace.ToDisplayString().Returns("Test.Namespace"); + var result = NameBuilder.GetNamespaceExtension(typeSymbol); + Assert.Equal("SafeTest.Namespace", result); + } - [Fact] - public void GetNamespace_Returns_Namespace_SafeExtension() - { - var typeSymbol = Substitute.For(); - typeSymbol.ContainingNamespace.ToDisplayString().Returns("Test.Namespace"); - var result = NameBuilder.GetNamespace(typeSymbol); - Assert.Equal("LaDeak.JsonMergePatch.Generated.SafeTest.Namespace", result); - } + [Fact] + public void GetNamespace_Returns_Namespace_SafeExtension() + { + var typeSymbol = Substitute.For(); + typeSymbol.ContainingNamespace.ToDisplayString().Returns("Test.Namespace"); + var result = NameBuilder.GetNamespace(typeSymbol); + Assert.Equal("LaDeak.JsonMergePatch.Generated.SafeTest.Namespace", result); + } - [Fact] - public void GetNamespaceOnString_Returns_Namespace_SafeExtension() - { - var result = NameBuilder.GetNamespace("Test.Namespace"); - Assert.Equal("LaDeak.JsonMergePatch.Generated.SafeTest.Namespace", result); - } + [Fact] + public void GetNamespaceOnString_Returns_Namespace_SafeExtension() + { + var result = NameBuilder.GetNamespace("Test.Namespace"); + Assert.Equal("LaDeak.JsonMergePatch.Generated.SafeTest.Namespace", result); + } - [Fact] - public void GetGetFullTypeName_Returns_Namespace_SafeExtension_TypeNameWrapped() - { - var typeSymbol = Substitute.For(); - typeSymbol.Name.Returns("TestName"); - typeSymbol.ContainingNamespace.ToDisplayString().Returns("Test.Namespace"); - var result = NameBuilder.GetFullTypeName(typeSymbol); - Assert.Equal("LaDeak.JsonMergePatch.Generated.SafeTest.Namespace.TestNameWrapped", result); - } + [Fact] + public void GetGetFullTypeName_Returns_Namespace_SafeExtension_TypeNameWrapped() + { + var typeSymbol = Substitute.For(); + typeSymbol.Name.Returns("TestName"); + typeSymbol.ContainingNamespace.ToDisplayString().Returns("Test.Namespace"); + var result = NameBuilder.GetFullTypeName(typeSymbol); + Assert.Equal("LaDeak.JsonMergePatch.Generated.SafeTest.Namespace.TestNameWrapped", result); } } diff --git a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/TypeBuilderTests.cs b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/TypeBuilderTests.cs index 7cd2bbe..1c7f947 100644 --- a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/TypeBuilderTests.cs +++ b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/TypeBuilderTests.cs @@ -1,88 +1,48 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; +using System.Collections.Immutable; using Microsoft.CodeAnalysis; using NSubstitute; using Xunit; -namespace LaDeak.JsonMergePatch.SourceGenerator.Tests +namespace LaDeak.JsonMergePatch.SourceGenerator.Tests; + +public class TypeBuilderTests { - public class TypeBuilderTests - { - [Fact] - public void BuildWrapperType_SourceTypeName_ReturnedInResultsSourceTypeName() - { - var sut = new TypeBuilder(); - var typeSymbol = Substitute.For(); - typeSymbol.BaseType.Returns((INamedTypeSymbol)null); - var attributes = ImmutableArray.Create(); - typeSymbol.GetAttributes().Returns(attributes); - var result = sut.BuildWrapperType(typeSymbol, "SourceName"); - Assert.Equal("SourceName", result.SourceTypeFullName); - } - - [Fact] - public void BuildWrapperType_TargetTypeName_ReturnedTypeNameAndWrapped() - { - var sut = new TypeBuilder(); - var typeSymbol = Substitute.For(); - typeSymbol.Name.Returns("TestType"); - typeSymbol.BaseType.Returns((INamedTypeSymbol)null); - typeSymbol.ContainingNamespace.ToDisplayString().Returns("TestTypeNamespace"); - var attributes = ImmutableArray.Create(); - typeSymbol.GetAttributes().Returns(attributes); - var result = sut.BuildWrapperType(typeSymbol, "SourceName"); - Assert.Equal("LaDeak.JsonMergePatch.Generated.SafeTestTypeNamespace.TestTypeWrapped", result.TargetTypeFullName); - } - - [Fact] - public void BuildWrapperType_ToProcessTypes_NotNull() - { - var sut = new TypeBuilder(); - var typeSymbol = Substitute.For(); - typeSymbol.BaseType.Returns((INamedTypeSymbol)null); - var attributes = ImmutableArray.Create(); - typeSymbol.GetAttributes().Returns(attributes); - var result = sut.BuildWrapperType(typeSymbol, "SourceName"); - Assert.NotNull(result.ToProcessTypes); - } - - [Fact] - public void BuildWrapperType_SourceCode_NotNull() - { - var sut = new TypeBuilder(); - var typeSymbol = Substitute.For(); - typeSymbol.BaseType.Returns((INamedTypeSymbol)null); - var attributes = ImmutableArray.Create(); - typeSymbol.GetAttributes().Returns(attributes); - var result = sut.BuildWrapperType(typeSymbol, "SourceName"); - Assert.NotNull(result.SourceCode); - } - - [Fact] - public void BuildWrapperType_FileName_UsesTypeName() - { - var sut = new TypeBuilder(); - var typeSymbol = Substitute.For(); - typeSymbol.Name.Returns("TestType"); - typeSymbol.BaseType.Returns((INamedTypeSymbol)null); - var attributes = ImmutableArray.Create(); - typeSymbol.GetAttributes().Returns(attributes); - var result = sut.BuildWrapperType(typeSymbol, "SourceName"); - Assert.Equal("LaDeakJsonMergePatchSafeTestTypeWrapped", result.FileName); - } - - [Fact] - public void EmptyType_Returns_EmptyTypeAndNamespace() - { - var sut = new TypeBuilder(); - var typeSymbol = Substitute.For(); - typeSymbol.Name.Returns("TestType"); - typeSymbol.BaseType.Returns((INamedTypeSymbol)null); - var attributes = ImmutableArray.Create(); - typeSymbol.GetAttributes().Returns(attributes); - var result = sut.BuildWrapperType(typeSymbol, "SourceName"); - Assert.Equal( + [Fact] + public void BuildWrapperType_SourceCode_NotNull() + { + var sut = new TypeBuilder(); + var typeSymbol = Substitute.For(); + typeSymbol.BaseType.Returns((INamedTypeSymbol)null); + var attributes = ImmutableArray.Create(); + typeSymbol.GetAttributes().Returns(attributes); + var result = sut.BuildWrapperType(new GeneratorClassInfo(typeSymbol, "SourceName")); + Assert.NotNull(result.SourceCode); + } + + [Fact] + public void BuildWrapperType_FileName_UsesTypeName() + { + var sut = new TypeBuilder(); + var typeSymbol = Substitute.For(); + typeSymbol.Name.Returns("TestType"); + typeSymbol.BaseType.Returns((INamedTypeSymbol)null); + var attributes = ImmutableArray.Create(); + typeSymbol.GetAttributes().Returns(attributes); + var result = sut.BuildWrapperType(new GeneratorClassInfo(typeSymbol, "SourceName")); + Assert.Equal("LaDeakJsonMergePatchSafeTestTypeWrapped", result.FileName); + } + + [Fact] + public void EmptyType_Returns_EmptyTypeAndNamespace() + { + var sut = new TypeBuilder(); + var typeSymbol = Substitute.For(); + typeSymbol.Name.Returns("TestType"); + typeSymbol.BaseType.Returns((INamedTypeSymbol)null); + var attributes = ImmutableArray.Create(); + typeSymbol.GetAttributes().Returns(attributes); + var result = sut.BuildWrapperType(new GeneratorClassInfo(typeSymbol, "SourceName")); + Assert.Equal( @"#nullable enable namespace LaDeak.JsonMergePatch.Generated.Safe { @@ -102,31 +62,31 @@ public override SourceName ApplyPatch([System.Diagnostics.CodeAnalysis.AllowNull } #nullable disable ", result.SourceCode); - } + } - [Fact] - public void TypeWithField_Returns_EmptyTypeAndNamespace() - { - var sut = new TypeBuilder(); - var typeSymbol = Substitute.For(); - typeSymbol.Name.Returns("TestType"); - typeSymbol.BaseType.Returns((INamedTypeSymbol)null); - var members = ImmutableArray.Create( - Substitute.For(), - Substitute.For(), - Substitute.For(), - Substitute.For(), - Substitute.For(), - Substitute.For(), - Substitute.For(), - Substitute.For(), - Substitute.For(), - Substitute.For()); - typeSymbol.GetMembers().Returns(members); - var attributes = ImmutableArray.Create(); - typeSymbol.GetAttributes().Returns(attributes); - var result = sut.BuildWrapperType(typeSymbol, "SourceName"); - Assert.Equal( + [Fact] + public void TypeWithField_Returns_EmptyTypeAndNamespace() + { + var sut = new TypeBuilder(); + var typeSymbol = Substitute.For(); + typeSymbol.Name.Returns("TestType"); + typeSymbol.BaseType.Returns((INamedTypeSymbol)null); + var members = ImmutableArray.Create( + Substitute.For(), + Substitute.For(), + Substitute.For(), + Substitute.For(), + Substitute.For(), + Substitute.For(), + Substitute.For(), + Substitute.For(), + Substitute.For(), + Substitute.For()); + typeSymbol.GetMembers().Returns(members); + var attributes = ImmutableArray.Create(); + typeSymbol.GetAttributes().Returns(attributes); + var result = sut.BuildWrapperType(new GeneratorClassInfo(typeSymbol, "SourceName")); + Assert.Equal( @"#nullable enable namespace LaDeak.JsonMergePatch.Generated.Safe { @@ -146,31 +106,31 @@ public override SourceName ApplyPatch([System.Diagnostics.CodeAnalysis.AllowNull } #nullable disable ", result.SourceCode); - } + } - [Fact] - public void ReadonlyWriteonlyStaticIndexerAbstract_Property_Ignored() - { - var sut = new TypeBuilder(); - var typeSymbol = Substitute.For(); - typeSymbol.Name.Returns("TestType"); - typeSymbol.BaseType.Returns((INamedTypeSymbol)null); - var readonlyProperty = Substitute.For(); - readonlyProperty.IsReadOnly.Returns(true); - var abstractProperty = Substitute.For(); - abstractProperty.IsAbstract.Returns(true); - var writeonlyProperty = Substitute.For(); - writeonlyProperty.IsWriteOnly.Returns(true); - var staticProperty = Substitute.For(); - staticProperty.IsStatic.Returns(true); - var indexerProperty = Substitute.For(); - indexerProperty.IsIndexer.Returns(true); - var members = ImmutableArray.Create(readonlyProperty, writeonlyProperty, staticProperty, indexerProperty, abstractProperty); - typeSymbol.GetMembers().Returns(members); - var attributes = ImmutableArray.Create(); - typeSymbol.GetAttributes().Returns(attributes); - var result = sut.BuildWrapperType(typeSymbol, "SourceName"); - Assert.Equal( + [Fact] + public void ReadonlyWriteonlyStaticIndexerAbstract_Property_Ignored() + { + var sut = new TypeBuilder(); + var typeSymbol = Substitute.For(); + typeSymbol.Name.Returns("TestType"); + typeSymbol.BaseType.Returns((INamedTypeSymbol)null); + var readonlyProperty = Substitute.For(); + readonlyProperty.IsReadOnly.Returns(true); + var abstractProperty = Substitute.For(); + abstractProperty.IsAbstract.Returns(true); + var writeonlyProperty = Substitute.For(); + writeonlyProperty.IsWriteOnly.Returns(true); + var staticProperty = Substitute.For(); + staticProperty.IsStatic.Returns(true); + var indexerProperty = Substitute.For(); + indexerProperty.IsIndexer.Returns(true); + var members = ImmutableArray.Create(readonlyProperty, writeonlyProperty, staticProperty, indexerProperty, abstractProperty); + typeSymbol.GetMembers().Returns(members); + var attributes = ImmutableArray.Create(); + typeSymbol.GetAttributes().Returns(attributes); + var result = sut.BuildWrapperType(new GeneratorClassInfo(typeSymbol, "SourceName")); + Assert.Equal( @"#nullable enable namespace LaDeak.JsonMergePatch.Generated.Safe { @@ -190,19 +150,19 @@ public override SourceName ApplyPatch([System.Diagnostics.CodeAnalysis.AllowNull } #nullable disable ", result.SourceCode); - } + } - [Fact] - public void ClassAttribute_Added_ToGeneratedClass() - { - var sut = new TypeBuilder(); - var typeSymbol = Substitute.For(); - typeSymbol.Name.Returns("TestType"); - typeSymbol.BaseType.Returns((INamedTypeSymbol)null); - var attributes = ImmutableArray.Create(new TestAttribute(), new TestAttribute("Hello")); - typeSymbol.GetAttributes().Returns(attributes); - var result = sut.BuildWrapperType(typeSymbol, "SourceName"); - Assert.Equal( + [Fact] + public void ClassAttribute_Added_ToGeneratedClass() + { + var sut = new TypeBuilder(); + var typeSymbol = Substitute.For(); + typeSymbol.Name.Returns("TestType"); + typeSymbol.BaseType.Returns((INamedTypeSymbol)null); + var attributes = ImmutableArray.Create(new TestAttribute(), new TestAttribute("Hello")); + typeSymbol.GetAttributes().Returns(attributes); + var result = sut.BuildWrapperType(new GeneratorClassInfo(typeSymbol, "SourceName")); + Assert.Equal( @"#nullable enable namespace LaDeak.JsonMergePatch.Generated.Safe { @@ -224,22 +184,22 @@ public override SourceName ApplyPatch([System.Diagnostics.CodeAnalysis.AllowNull } #nullable disable ", result.SourceCode); - } + } - [Fact] - public void ReadWriteProperty_Added_BackingFieldAndProperty() - { - var sut = new TypeBuilder(); - var typeSymbol = Substitute.For(); - typeSymbol.Name.Returns("TestType"); - typeSymbol.BaseType.Returns((INamedTypeSymbol)null); - typeSymbol.BaseType.Returns((INamedTypeSymbol)null); - var property = GetProperty("System", "String", "TestProp"); - typeSymbol.GetMembers().Returns(ImmutableArray.Create(property)); - var attributes = ImmutableArray.Create(); - typeSymbol.GetAttributes().Returns(attributes); - var result = sut.BuildWrapperType(typeSymbol, "SourceName"); - Assert.Equal( + [Fact] + public void ReadWriteProperty_Added_BackingFieldAndProperty() + { + var sut = new TypeBuilder(); + var typeSymbol = Substitute.For(); + typeSymbol.Name.Returns("TestType"); + typeSymbol.BaseType.Returns((INamedTypeSymbol)null); + typeSymbol.BaseType.Returns((INamedTypeSymbol)null); + var property = GetProperty("System", "String", "TestProp"); + typeSymbol.GetMembers().Returns(ImmutableArray.Create(property)); + var attributes = ImmutableArray.Create(); + typeSymbol.GetAttributes().Returns(attributes); + var result = sut.BuildWrapperType(new GeneratorClassInfo(typeSymbol, "SourceName")); + Assert.Equal( @"#nullable enable namespace LaDeak.JsonMergePatch.Generated.Safe { @@ -272,24 +232,23 @@ public override SourceName ApplyPatch([System.Diagnostics.CodeAnalysis.AllowNull } #nullable disable ", result.SourceCode); - Assert.Contains(result.ToProcessTypes, x => x.ToDisplayString() == "System.String"); - } - - [Fact] - public void MultipleReadWriteProperty_Added_BackingFieldsAndProperties() - { - var sut = new TypeBuilder(); - var typeSymbol = Substitute.For(); - typeSymbol.Name.Returns("TestType"); - typeSymbol.BaseType.Returns((INamedTypeSymbol)null); - var prop0 = GetProperty("System", "String", "TestProp0"); - var prop1 = GetProperty("System", "Int32", "TestProp1"); - typeSymbol.GetMembers().Returns(ImmutableArray.Create(prop0, prop1)); - var attributes = ImmutableArray.Create(); - typeSymbol.GetAttributes().Returns(attributes); - var result = sut.BuildWrapperType(typeSymbol, "SourceName"); - Assert.Equal( - @"#nullable enable + } + + [Fact] + public void MultipleReadWriteProperty_Added_BackingFieldsAndProperties() + { + var sut = new TypeBuilder(); + var typeSymbol = Substitute.For(); + typeSymbol.Name.Returns("TestType"); + typeSymbol.BaseType.Returns((INamedTypeSymbol)null); + var prop0 = GetProperty("System", "String", "TestProp0"); + var prop1 = GetProperty("System", "Int32", "TestProp1"); + typeSymbol.GetMembers().Returns(ImmutableArray.Create(prop0, prop1)); + var attributes = ImmutableArray.Create(); + typeSymbol.GetAttributes().Returns(attributes); + var result = sut.BuildWrapperType(new GeneratorClassInfo(typeSymbol, "SourceName")); + Assert.Equal( +@"#nullable enable namespace LaDeak.JsonMergePatch.Generated.Safe { public class TestTypeWrapped : LaDeak.JsonMergePatch.Abstractions.Patch @@ -334,23 +293,23 @@ public override SourceName ApplyPatch([System.Diagnostics.CodeAnalysis.AllowNull } #nullable disable ", result.SourceCode); - } + } - [Fact] - public void MultipleReadWriteProperty_AddsTypeDataToProcessSymbols() - { - var sut = new TypeBuilder(); - var typeSymbol = Substitute.For(); - typeSymbol.Name.Returns("TestType"); - typeSymbol.BaseType.Returns((INamedTypeSymbol)null); - var prop0 = GetProperty("Test", "Dto", "TestProp0"); - var prop1 = GetProperty("System", "Int32", "TestProp1"); - typeSymbol.GetMembers().Returns(ImmutableArray.Create(prop0, prop1)); - var attributes = ImmutableArray.Create(); - typeSymbol.GetAttributes().Returns(attributes); - var result = sut.BuildWrapperType(typeSymbol, "SourceName"); - Assert.Equal( - @"#nullable enable + [Fact] + public void MultipleReadWriteProperty_AddsTypeDataToProcessSymbols() + { + var sut = new TypeBuilder(); + var typeSymbol = Substitute.For(); + typeSymbol.Name.Returns("TestType"); + typeSymbol.BaseType.Returns((INamedTypeSymbol)null); + var prop0 = GetProperty("Test", "Dto", "TestProp0"); + var prop1 = GetProperty("System", "Int32", "TestProp1"); + typeSymbol.GetMembers().Returns(ImmutableArray.Create(prop0, prop1)); + var attributes = ImmutableArray.Create(); + typeSymbol.GetAttributes().Returns(attributes); + var result = sut.BuildWrapperType(new GeneratorClassInfo(typeSymbol, "SourceName")); + Assert.Equal( +@"#nullable enable namespace LaDeak.JsonMergePatch.Generated.Safe { public class TestTypeWrapped : LaDeak.JsonMergePatch.Abstractions.Patch @@ -395,23 +354,21 @@ public override SourceName ApplyPatch([System.Diagnostics.CodeAnalysis.AllowNull } #nullable disable ", result.SourceCode); - Assert.Contains(result.ToProcessTypes, x => x.ToDisplayString() == "Test.Dto"); - Assert.Contains(result.ToProcessTypes, x => x.ToDisplayString() == "System.Int32"); - } - - [Fact] - public void PropertyWitAttribute_Adds_AttributeOnProperty() - { - var sut = new TypeBuilder(); - var typeSymbol = Substitute.For(); - typeSymbol.Name.Returns("TestType"); - typeSymbol.BaseType.Returns((INamedTypeSymbol)null); - var property = GetProperty("System", "String", "TestProp", new TestAttribute("JsonPropertyName(\"temp\")")); - typeSymbol.GetMembers().Returns(ImmutableArray.Create(property)); - var attributes = ImmutableArray.Create(); - typeSymbol.GetAttributes().Returns(attributes); - var result = sut.BuildWrapperType(typeSymbol, "SourceName"); - Assert.Equal( + } + + [Fact] + public void PropertyWitAttribute_Adds_AttributeOnProperty() + { + var sut = new TypeBuilder(); + var typeSymbol = Substitute.For(); + typeSymbol.Name.Returns("TestType"); + typeSymbol.BaseType.Returns((INamedTypeSymbol)null); + var property = GetProperty("System", "String", "TestProp", new TestAttribute("JsonPropertyName(\"temp\")")); + typeSymbol.GetMembers().Returns(ImmutableArray.Create(property)); + var attributes = ImmutableArray.Create(); + typeSymbol.GetAttributes().Returns(attributes); + var result = sut.BuildWrapperType(new GeneratorClassInfo(typeSymbol, "SourceName")); + Assert.Equal( @"#nullable enable namespace LaDeak.JsonMergePatch.Generated.Safe { @@ -445,20 +402,20 @@ public override SourceName ApplyPatch([System.Diagnostics.CodeAnalysis.AllowNull } #nullable disable ", result.SourceCode); - } + } - [Fact] - public void DictionaryWithValueProperty_CreatesPatchToPatchEachValue() - { - var sut = new TypeBuilder(); - var typeSymbol = Substitute.For(); - typeSymbol.Name.Returns("TestType"); - typeSymbol.BaseType.Returns((INamedTypeSymbol)null); - var property = GetGenericProperty("System.Collections.Generic", "Dictionary", "TestProp", GetType("System", "String"), GetType("System", "Int32")); - typeSymbol.GetMembers().Returns(ImmutableArray.Create(property)); - typeSymbol.GetAttributes().Returns(ImmutableArray.Create()); - var result = sut.BuildWrapperType(typeSymbol, "SourceName"); - Assert.Equal( + [Fact] + public void DictionaryWithValueProperty_CreatesPatchToPatchEachValue() + { + var sut = new TypeBuilder(); + var typeSymbol = Substitute.For(); + typeSymbol.Name.Returns("TestType"); + typeSymbol.BaseType.Returns((INamedTypeSymbol)null); + var property = GetGenericProperty("System.Collections.Generic", "Dictionary", "TestProp", GetType("System", "String"), GetType("System", "Int32")); + typeSymbol.GetMembers().Returns(ImmutableArray.Create(property)); + typeSymbol.GetAttributes().Returns(ImmutableArray.Create()); + var result = sut.BuildWrapperType(new GeneratorClassInfo(typeSymbol, "SourceName")); + Assert.Equal( @"#nullable enable namespace LaDeak.JsonMergePatch.Generated.Safe { @@ -501,21 +458,21 @@ public override SourceName ApplyPatch([System.Diagnostics.CodeAnalysis.AllowNull } #nullable disable ", result.SourceCode); - } + } - [Fact] - public void MultipleDictionaryProperty_CreatesPatchToPatchEachValue() - { - var sut = new TypeBuilder(); - var typeSymbol = Substitute.For(); - typeSymbol.Name.Returns("TestType"); - typeSymbol.BaseType.Returns((INamedTypeSymbol)null); - var property0 = GetGenericProperty("System.Collections.Generic", "Dictionary", "TestProp0", GetType("System", "String"), GetType("System", "Int32")); - var property1 = GetGenericProperty("System.Collections.Generic", "Dictionary", "TestProp1", GetType("System", "String"), GetType("System", "Int32")); - typeSymbol.GetMembers().Returns(ImmutableArray.Create(property0, property1)); - typeSymbol.GetAttributes().Returns(ImmutableArray.Create()); - var result = sut.BuildWrapperType(typeSymbol, "SourceName"); - Assert.Equal( + [Fact] + public void MultipleDictionaryProperty_CreatesPatchToPatchEachValue() + { + var sut = new TypeBuilder(); + var typeSymbol = Substitute.For(); + typeSymbol.Name.Returns("TestType"); + typeSymbol.BaseType.Returns((INamedTypeSymbol)null); + var property0 = GetGenericProperty("System.Collections.Generic", "Dictionary", "TestProp0", GetType("System", "String"), GetType("System", "Int32")); + var property1 = GetGenericProperty("System.Collections.Generic", "Dictionary", "TestProp1", GetType("System", "String"), GetType("System", "Int32")); + typeSymbol.GetMembers().Returns(ImmutableArray.Create(property0, property1)); + typeSymbol.GetAttributes().Returns(ImmutableArray.Create()); + var result = sut.BuildWrapperType(new GeneratorClassInfo(typeSymbol, "SourceName")); + Assert.Equal( @"#nullable enable namespace LaDeak.JsonMergePatch.Generated.Safe { @@ -581,20 +538,20 @@ public override SourceName ApplyPatch([System.Diagnostics.CodeAnalysis.AllowNull } #nullable disable ", result.SourceCode); - } + } - [Fact] - public void DictionaryWithReferenceProperty_CreatesPatchToPatchEachValue() - { - var sut = new TypeBuilder(); - var typeSymbol = Substitute.For(); - typeSymbol.Name.Returns("TestType"); - typeSymbol.BaseType.Returns((INamedTypeSymbol)null); - var property = GetGenericProperty("System.Collections.Generic", "Dictionary", "TestProp", GetType("System", "String"), GetType("System", "String")); - typeSymbol.GetMembers().Returns(ImmutableArray.Create(property)); - typeSymbol.GetAttributes().Returns(ImmutableArray.Create()); - var result = sut.BuildWrapperType(typeSymbol, "SourceName"); - Assert.Equal( + [Fact] + public void DictionaryWithReferenceProperty_CreatesPatchToPatchEachValue() + { + var sut = new TypeBuilder(); + var typeSymbol = Substitute.For(); + typeSymbol.Name.Returns("TestType"); + typeSymbol.BaseType.Returns((INamedTypeSymbol)null); + var property = GetGenericProperty("System.Collections.Generic", "Dictionary", "TestProp", GetType("System", "String"), GetType("System", "String")); + typeSymbol.GetMembers().Returns(ImmutableArray.Create(property)); + typeSymbol.GetAttributes().Returns(ImmutableArray.Create()); + var result = sut.BuildWrapperType(new GeneratorClassInfo(typeSymbol, "SourceName")); + Assert.Equal( @"#nullable enable namespace LaDeak.JsonMergePatch.Generated.Safe { @@ -637,20 +594,20 @@ public override SourceName ApplyPatch([System.Diagnostics.CodeAnalysis.AllowNull } #nullable disable ", result.SourceCode); - } + } - [Fact] - public void DictionaryWithNullableValueProperty_CreatesPatchToPatchEachValue() - { - var sut = new TypeBuilder(); - var typeSymbol = Substitute.For(); - typeSymbol.Name.Returns("TestType"); - typeSymbol.BaseType.Returns((INamedTypeSymbol)null); - var property = GetGenericProperty("System.Collections.Generic", "Dictionary", "TestProp", GetType("System", "String"), GetNullableType(GetType("System", "Int32"))); - typeSymbol.GetMembers().Returns(ImmutableArray.Create(property)); - typeSymbol.GetAttributes().Returns(ImmutableArray.Create()); - var result = sut.BuildWrapperType(typeSymbol, "SourceName"); - Assert.Equal( + [Fact] + public void DictionaryWithNullableValueProperty_CreatesPatchToPatchEachValue() + { + var sut = new TypeBuilder(); + var typeSymbol = Substitute.For(); + typeSymbol.Name.Returns("TestType"); + typeSymbol.BaseType.Returns((INamedTypeSymbol)null); + var property = GetGenericProperty("System.Collections.Generic", "Dictionary", "TestProp", GetType("System", "String"), GetNullableType(GetType("System", "Int32"))); + typeSymbol.GetMembers().Returns(ImmutableArray.Create(property)); + typeSymbol.GetAttributes().Returns(ImmutableArray.Create()); + var result = sut.BuildWrapperType(new GeneratorClassInfo(typeSymbol, "SourceName")); + Assert.Equal( @"#nullable enable namespace LaDeak.JsonMergePatch.Generated.Safe { @@ -693,20 +650,20 @@ public override SourceName ApplyPatch([System.Diagnostics.CodeAnalysis.AllowNull } #nullable disable ", result.SourceCode); - } + } - [Fact] - public void DictionaryWithNullableReferenceProperty_CreatesPatchToPatchEachValue() - { - var sut = new TypeBuilder(); - var typeSymbol = Substitute.For(); - typeSymbol.Name.Returns("TestType"); - typeSymbol.BaseType.Returns((INamedTypeSymbol)null); - var property = GetGenericProperty("System.Collections.Generic", "Dictionary", "TestProp", GetType("System", "String"), GetNullableReferenceType(GetType("System", "String"))); - typeSymbol.GetMembers().Returns(ImmutableArray.Create(property)); - typeSymbol.GetAttributes().Returns(ImmutableArray.Create()); - var result = sut.BuildWrapperType(typeSymbol, "SourceName"); - Assert.Equal( + [Fact] + public void DictionaryWithNullableReferenceProperty_CreatesPatchToPatchEachValue() + { + var sut = new TypeBuilder(); + var typeSymbol = Substitute.For(); + typeSymbol.Name.Returns("TestType"); + typeSymbol.BaseType.Returns((INamedTypeSymbol)null); + var property = GetGenericProperty("System.Collections.Generic", "Dictionary", "TestProp", GetType("System", "String"), GetNullableReferenceType(GetType("System", "String"))); + typeSymbol.GetMembers().Returns(ImmutableArray.Create(property)); + typeSymbol.GetAttributes().Returns(ImmutableArray.Create()); + var result = sut.BuildWrapperType(new GeneratorClassInfo(typeSymbol, "SourceName")); + Assert.Equal( @"#nullable enable namespace LaDeak.JsonMergePatch.Generated.Safe { @@ -749,27 +706,27 @@ public override SourceName ApplyPatch([System.Diagnostics.CodeAnalysis.AllowNull } #nullable disable ", result.SourceCode); - } - - [Fact] - public void PropertiesInBaseTypeIncluded() - { - var sut = new TypeBuilder(); - var typeSymbol = Substitute.For(); - typeSymbol.Name.Returns("TestType"); - var property = GetProperty("System", "String", "TestProp"); - - var baseTypeSymbol = Substitute.For(); - baseTypeSymbol.BaseType.Returns((INamedTypeSymbol)null); - var propertyBase = GetProperty("System", "Int32", "TestPropBase"); - baseTypeSymbol.GetMembers().Returns(ImmutableArray.Create(propertyBase)); - typeSymbol.BaseType.Returns(baseTypeSymbol); + } - typeSymbol.GetMembers().Returns(ImmutableArray.Create(property)); - var attributes = ImmutableArray.Create(); - typeSymbol.GetAttributes().Returns(attributes); - var result = sut.BuildWrapperType(typeSymbol, "SourceName"); - Assert.Equal( + [Fact] + public void PropertiesInBaseTypeIncluded() + { + var sut = new TypeBuilder(); + var typeSymbol = Substitute.For(); + typeSymbol.Name.Returns("TestType"); + var property = GetProperty("System", "String", "TestProp"); + + var baseTypeSymbol = Substitute.For(); + baseTypeSymbol.BaseType.Returns((INamedTypeSymbol)null); + var propertyBase = GetProperty("System", "Int32", "TestPropBase"); + baseTypeSymbol.GetMembers().Returns(ImmutableArray.Create(propertyBase)); + typeSymbol.BaseType.Returns(baseTypeSymbol); + + typeSymbol.GetMembers().Returns(ImmutableArray.Create(property)); + var attributes = ImmutableArray.Create(); + typeSymbol.GetAttributes().Returns(attributes); + var result = sut.BuildWrapperType(new GeneratorClassInfo(typeSymbol, "SourceName")); + Assert.Equal( @"#nullable enable namespace LaDeak.JsonMergePatch.Generated.Safe { @@ -815,21 +772,20 @@ public override SourceName ApplyPatch([System.Diagnostics.CodeAnalysis.AllowNull } #nullable disable ", result.SourceCode); - Assert.Contains(result.ToProcessTypes, x => x.ToDisplayString() == "System.String"); - } + } - [Fact] - public void ListOfComplexType_CreatesPatchToPatchEachValue() - { - var sut = new TypeBuilder(); - var typeSymbol = Substitute.For(); - typeSymbol.Name.Returns("TestType"); - typeSymbol.BaseType.Returns((INamedTypeSymbol)null); - var property = GetGenericProperty("System.Collections.Generic", "List", "TestProp", GetType("Test", "Dto")); - typeSymbol.GetMembers().Returns(ImmutableArray.Create(property)); - typeSymbol.GetAttributes().Returns(ImmutableArray.Create()); - var result = sut.BuildWrapperType(typeSymbol, "SourceName"); - Assert.Equal( + [Fact] + public void ListOfComplexType_CreatesPatchToPatchEachValue() + { + var sut = new TypeBuilder(); + var typeSymbol = Substitute.For(); + typeSymbol.Name.Returns("TestType"); + typeSymbol.BaseType.Returns((INamedTypeSymbol)null); + var property = GetGenericProperty("System.Collections.Generic", "List", "TestProp", GetType("Test", "Dto")); + typeSymbol.GetMembers().Returns(ImmutableArray.Create(property)); + typeSymbol.GetAttributes().Returns(ImmutableArray.Create()); + var result = sut.BuildWrapperType(new GeneratorClassInfo(typeSymbol, "SourceName")); + Assert.Equal( @"#nullable enable namespace LaDeak.JsonMergePatch.Generated.Safe { @@ -869,20 +825,20 @@ public override SourceName ApplyPatch([System.Diagnostics.CodeAnalysis.AllowNull } #nullable disable ", result.SourceCode); - } + } - [Fact] - public void ListOfKnownType_CreatesPatchToPatchEachValue() - { - var sut = new TypeBuilder(); - var typeSymbol = Substitute.For(); - typeSymbol.Name.Returns("TestType"); - typeSymbol.BaseType.Returns((INamedTypeSymbol)null); - var property = GetGenericProperty("System.Collections.Generic", "List", "TestProp", GetType("System", "String")); - typeSymbol.GetMembers().Returns(ImmutableArray.Create(property)); - typeSymbol.GetAttributes().Returns(ImmutableArray.Create()); - var result = sut.BuildWrapperType(typeSymbol, "SourceName"); - Assert.Equal( + [Fact] + public void ListOfKnownType_CreatesPatchToPatchEachValue() + { + var sut = new TypeBuilder(); + var typeSymbol = Substitute.For(); + typeSymbol.Name.Returns("TestType"); + typeSymbol.BaseType.Returns((INamedTypeSymbol)null); + var property = GetGenericProperty("System.Collections.Generic", "List", "TestProp", GetType("System", "String")); + typeSymbol.GetMembers().Returns(ImmutableArray.Create(property)); + typeSymbol.GetAttributes().Returns(ImmutableArray.Create()); + var result = sut.BuildWrapperType(new GeneratorClassInfo(typeSymbol, "SourceName")); + Assert.Equal( @"#nullable enable namespace LaDeak.JsonMergePatch.Generated.Safe { @@ -915,98 +871,97 @@ public override SourceName ApplyPatch([System.Diagnostics.CodeAnalysis.AllowNull } #nullable disable ", result.SourceCode); - } + } - private ITypeSymbol GetType(string namespaceName, string typeName) - { - var propertyTypeSymbol = Substitute.For(); - propertyTypeSymbol.Name.Returns(typeName); - SpecialType specialType = GetSpecialTypeFlag(typeName); - propertyTypeSymbol.SpecialType.Returns(specialType); - propertyTypeSymbol.ToDisplayString().ReturnsForAnyArgs($"{namespaceName}.{typeName}"); - var namespaceSymbol = Substitute.For(); - namespaceSymbol.ToDisplayString().Returns(namespaceName); - propertyTypeSymbol.ContainingNamespace.Returns(namespaceSymbol); - propertyTypeSymbol.IsValueType.Returns(GetIsValueTypeFlag(typeName)); - propertyTypeSymbol.NullableAnnotation.Returns(NullableAnnotation.NotAnnotated); - return propertyTypeSymbol; - } + private ITypeSymbol GetType(string namespaceName, string typeName) + { + var propertyTypeSymbol = Substitute.For(); + propertyTypeSymbol.Name.Returns(typeName); + SpecialType specialType = GetSpecialTypeFlag(typeName); + propertyTypeSymbol.SpecialType.Returns(specialType); + propertyTypeSymbol.ToDisplayString().ReturnsForAnyArgs($"{namespaceName}.{typeName}"); + var namespaceSymbol = Substitute.For(); + namespaceSymbol.ToDisplayString().Returns(namespaceName); + propertyTypeSymbol.ContainingNamespace.Returns(namespaceSymbol); + propertyTypeSymbol.IsValueType.Returns(GetIsValueTypeFlag(typeName)); + propertyTypeSymbol.NullableAnnotation.Returns(NullableAnnotation.NotAnnotated); + return propertyTypeSymbol; + } - private INamedTypeSymbol GetNullableType(ITypeSymbol typeParameter) - { - var type = Substitute.For(); - type.IsGenericType.Returns(true); - type.IsAnonymousType.Returns(false); - type.IsAbstract.Returns(false); - type.SpecialType.Returns(SpecialType.System_Nullable_T); - type.TypeArguments.Returns(ImmutableArray.Create(typeParameter)); - string name = $"System.Nullable<{typeParameter.ToDisplayString()}>"; - type.ToDisplayString(GeneratedTypeFilter.SymbolFormat).ReturnsForAnyArgs(name); - type.NullableAnnotation.Returns(NullableAnnotation.Annotated); - return type; - } + private INamedTypeSymbol GetNullableType(ITypeSymbol typeParameter) + { + var type = Substitute.For(); + type.IsGenericType.Returns(true); + type.IsAnonymousType.Returns(false); + type.IsAbstract.Returns(false); + type.SpecialType.Returns(SpecialType.System_Nullable_T); + type.TypeArguments.Returns(ImmutableArray.Create(typeParameter)); + string name = $"System.Nullable<{typeParameter.ToDisplayString()}>"; + type.ToDisplayString(GeneratedTypeFilter.SymbolFormat).ReturnsForAnyArgs(name); + type.NullableAnnotation.Returns(NullableAnnotation.Annotated); + return type; + } - private ITypeSymbol GetNullableReferenceType(ITypeSymbol typeParameter) - { - typeParameter.NullableAnnotation.Returns(NullableAnnotation.Annotated); - return typeParameter; - } + private ITypeSymbol GetNullableReferenceType(ITypeSymbol typeParameter) + { + typeParameter.NullableAnnotation.Returns(NullableAnnotation.Annotated); + return typeParameter; + } - private IPropertySymbol GetProperty(string namespaceName, string typeName, string name, AttributeData attribute = null) - { - var propertyTypeSymbol = GetType(namespaceName, typeName); - var property = Substitute.For(); - property.Name.Returns(name); - property.Type.Returns(propertyTypeSymbol); - property.GetAttributes().Returns(attribute == null ? ImmutableArray.Create() : ImmutableArray.Create(attribute)); - return property; - } + private IPropertySymbol GetProperty(string namespaceName, string typeName, string name, AttributeData attribute = null) + { + var propertyTypeSymbol = GetType(namespaceName, typeName); + var property = Substitute.For(); + property.Name.Returns(name); + property.Type.Returns(propertyTypeSymbol); + property.GetAttributes().Returns(attribute == null ? ImmutableArray.Create() : ImmutableArray.Create(attribute)); + return property; + } - private IPropertySymbol GetGenericProperty(string namespaceName, string typeName, string name, ITypeSymbol propertyTypeSymbol0, ITypeSymbol propertyTypeSymbol1) - { - return GetGenericProperty(namespaceName, typeName, name, new[] { propertyTypeSymbol0, propertyTypeSymbol1 }); - } + private IPropertySymbol GetGenericProperty(string namespaceName, string typeName, string name, ITypeSymbol propertyTypeSymbol0, ITypeSymbol propertyTypeSymbol1) + { + return GetGenericProperty(namespaceName, typeName, name, new[] { propertyTypeSymbol0, propertyTypeSymbol1 }); + } - private IPropertySymbol GetGenericProperty(string namespaceName, string typeName, string name, ITypeSymbol propertyTypeSymbol0) - { - return GetGenericProperty(namespaceName, typeName, name, new[] { propertyTypeSymbol0 }); - } + private IPropertySymbol GetGenericProperty(string namespaceName, string typeName, string name, ITypeSymbol propertyTypeSymbol0) + { + return GetGenericProperty(namespaceName, typeName, name, new[] { propertyTypeSymbol0 }); + } + + private IPropertySymbol GetGenericProperty(string namespaceName, string typeName, string name, ITypeSymbol[] genericTypes) + { + var propertyTypeSymbol = Substitute.For(); + propertyTypeSymbol.Name.Returns(typeName); + SpecialType specialType = GetSpecialTypeFlag(typeName); + propertyTypeSymbol.SpecialType.Returns(specialType); + propertyTypeSymbol.ToDisplayString().ReturnsForAnyArgs($"{namespaceName}.{typeName}"); + var namespaceSymbol = Substitute.For(); + namespaceSymbol.ToDisplayString().Returns(namespaceName); + propertyTypeSymbol.ContainingNamespace.Returns(namespaceSymbol); + propertyTypeSymbol.IsValueType.Returns(GetIsValueTypeFlag(typeName)); + propertyTypeSymbol.IsGenericType.Returns(true); + propertyTypeSymbol.TypeArguments.Returns(ImmutableArray.Create(genericTypes, 0, genericTypes.Length)); + var property = Substitute.For(); + property.Name.Returns(name); + property.Type.Returns(propertyTypeSymbol); + property.GetAttributes().Returns(ImmutableArray.Create()); + return property; + } - private IPropertySymbol GetGenericProperty(string namespaceName, string typeName, string name, ITypeSymbol[] genericTypes) + private SpecialType GetSpecialTypeFlag(string typeName) => + typeName switch { - var propertyTypeSymbol = Substitute.For(); - propertyTypeSymbol.Name.Returns(typeName); - SpecialType specialType = GetSpecialTypeFlag(typeName); - propertyTypeSymbol.SpecialType.Returns(specialType); - propertyTypeSymbol.ToDisplayString().ReturnsForAnyArgs($"{namespaceName}.{typeName}"); - var namespaceSymbol = Substitute.For(); - namespaceSymbol.ToDisplayString().Returns(namespaceName); - propertyTypeSymbol.ContainingNamespace.Returns(namespaceSymbol); - propertyTypeSymbol.IsValueType.Returns(GetIsValueTypeFlag(typeName)); - propertyTypeSymbol.IsGenericType.Returns(true); - propertyTypeSymbol.TypeArguments.Returns(ImmutableArray.Create(genericTypes, 0, genericTypes.Length)); - var property = Substitute.For(); - property.Name.Returns(name); - property.Type.Returns(propertyTypeSymbol); - property.GetAttributes().Returns(ImmutableArray.Create()); - return property; - } + "Int32" => SpecialType.System_Int32, + "String" => SpecialType.System_String, + "Dictionary" => SpecialType.None, + _ => SpecialType.None, + }; - private SpecialType GetSpecialTypeFlag(string typeName) => - typeName switch - { - "Int32" => SpecialType.System_Int32, - "String" => SpecialType.System_String, - "Dictionary" => SpecialType.None, - _ => SpecialType.None, - }; - - private bool GetIsValueTypeFlag(string typeName) => - typeName switch - { - "Int32" => true, - "String" => false, - _ => false, - }; - } + private bool GetIsValueTypeFlag(string typeName) => + typeName switch + { + "Int32" => true, + "String" => false, + _ => false, + }; } \ No newline at end of file diff --git a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/TypeRepositoryGeneratorTests.cs b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/TypeRepositoryGeneratorTests.cs index 66b2a8f..893e707 100644 --- a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/TypeRepositoryGeneratorTests.cs +++ b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/TypeRepositoryGeneratorTests.cs @@ -6,16 +6,16 @@ using Microsoft.CodeAnalysis; using Xunit; -namespace LaDeak.JsonMergePatch.AspNetCore.Tests +namespace LaDeak.JsonMergePatch.AspNetCore.Tests; + +public class TypeRepositoryGeneratorTests { - public class TypeRepositoryGeneratorTests + [Fact] + public void EmptyInput_CreateRepository_GeneratesEmptyConstructor() { - [Fact] - public void EmptyInput_CreateRepository_GeneratesEmptyConstructor() - { - var sut = new TypeRepositoryGenerator(); - var result = sut.CreateTypeRepository(null, string.Empty); - Assert.Equal(@" + var sut = new TypeRepositoryGenerator(); + var result = sut.CreateTypeRepository(null, string.Empty); + Assert.Equal(@" namespace LaDeak.JsonMergePatch.Generated.Safe { public class TypeRepository : LaDeak.JsonMergePatch.Abstractions.ITypeRepository @@ -48,14 +48,14 @@ public bool TryGet(System.Type source, [System.Diagnostics.CodeAnalysis.NotNullW public System.Collections.Generic.IEnumerable> GetAll() => _repository; } }", result); - } + } - [Fact] - public void CustomNamespace_CreateRepository_AppendsSAvoidCollision() - { - var sut = new TypeRepositoryGenerator(); - var result = sut.CreateTypeRepository(null, "CustomNamespace"); - Assert.Equal(@" + [Fact] + public void CustomNamespace_CreateRepository_AppendsSAvoidCollision() + { + var sut = new TypeRepositoryGenerator(); + var result = sut.CreateTypeRepository(null, "CustomNamespace"); + Assert.Equal(@" namespace LaDeak.JsonMergePatch.Generated.SafeCustomNamespace { public class TypeRepository : LaDeak.JsonMergePatch.Abstractions.ITypeRepository @@ -88,14 +88,14 @@ public bool TryGet(System.Type source, [System.Diagnostics.CodeAnalysis.NotNullW public System.Collections.Generic.IEnumerable> GetAll() => _repository; } }", result); - } + } - [Fact] - public void WithTypes_CreateRepository_GeneratesEmptyConstructor() - { - var sut = new TypeRepositoryGenerator(); - var result = sut.CreateTypeRepository(new[] { ("TestDto0", "TestDto0Wrapped"), ("TestDto1", "TestDto1Wrapped") }, string.Empty); - Assert.Equal(@" + [Fact] + public void WithTypes_CreateRepository_GeneratesEmptyConstructor() + { + var sut = new TypeRepositoryGenerator(); + var result = sut.CreateTypeRepository(new[] { ("TestDto0", "TestDto0Wrapped"), ("TestDto1", "TestDto1Wrapped") }, string.Empty); + Assert.Equal(@" namespace LaDeak.JsonMergePatch.Generated.Safe { public class TypeRepository : LaDeak.JsonMergePatch.Abstractions.ITypeRepository @@ -130,102 +130,101 @@ public bool TryGet(System.Type source, [System.Diagnostics.CodeAnalysis.NotNullW public System.Collections.Generic.IEnumerable> GetAll() => _repository; } }", result); - } + } - [Fact] - public void EmptyInput_CreateRepository_Compiles() - { - var sut = new TypeRepositoryGenerator(); - _ = Compile(sut.CreateTypeRepository(null, string.Empty)); - } + [Fact] + public void EmptyInput_CreateRepository_Compiles() + { + var sut = new TypeRepositoryGenerator(); + _ = Compile(sut.CreateTypeRepository(null, string.Empty)); + } - [Fact] - public void Compiled_Repository_ReturnsInstanceSingleton() - { - var sut = new TypeRepositoryGenerator(); - var typeRepository = GetTypeRepository(Compile(sut.CreateTypeRepository(null, string.Empty))); - Assert.NotNull(typeRepository); - } + [Fact] + public void Compiled_Repository_ReturnsInstanceSingleton() + { + var sut = new TypeRepositoryGenerator(); + var typeRepository = GetTypeRepository(Compile(sut.CreateTypeRepository(null, string.Empty))); + Assert.NotNull(typeRepository); + } - [Fact] - public void EmptyRepository_Add_DoesNotThrow() - { - var generator = new TypeRepositoryGenerator(); - var sut = GetTypeRepository(Compile(generator.CreateTypeRepository(null, string.Empty))); - sut.Add(); - } + [Fact] + public void EmptyRepository_Add_DoesNotThrow() + { + var generator = new TypeRepositoryGenerator(); + var sut = GetTypeRepository(Compile(generator.CreateTypeRepository(null, string.Empty))); + sut.Add(); + } - [Fact] - public void EmptyRepository_AddTwiceSameType_ThrowsException() - { - var generator = new TypeRepositoryGenerator(); - var sut = GetTypeRepository(Compile(generator.CreateTypeRepository(null, string.Empty))); - sut.Add(); - Assert.Throws(() => sut.Add()); - } + [Fact] + public void EmptyRepository_AddTwiceSameType_ThrowsException() + { + var generator = new TypeRepositoryGenerator(); + var sut = GetTypeRepository(Compile(generator.CreateTypeRepository(null, string.Empty))); + sut.Add(); + Assert.Throws(() => sut.Add()); + } - [Fact] - public void EmptyRepository_TryGet_ReturnsFalse() - { - var generator = new TypeRepositoryGenerator(); - var sut = GetTypeRepository(Compile(generator.CreateTypeRepository(null, string.Empty))); - Assert.False(sut.TryGet(typeof(TestDto), out _)); - } + [Fact] + public void EmptyRepository_TryGet_ReturnsFalse() + { + var generator = new TypeRepositoryGenerator(); + var sut = GetTypeRepository(Compile(generator.CreateTypeRepository(null, string.Empty))); + Assert.False(sut.TryGet(typeof(TestDto), out _)); + } - [Fact] - public void RepositoryWitTestDto_TryGet_ReturnsTrue() - { - var generator = new TypeRepositoryGenerator(); - var sut = GetTypeRepository(Compile(generator.CreateTypeRepository(null, string.Empty))); - sut.Add(); - Assert.True(sut.TryGet(typeof(TestDto), out _)); - } + [Fact] + public void RepositoryWitTestDto_TryGet_ReturnsTrue() + { + var generator = new TypeRepositoryGenerator(); + var sut = GetTypeRepository(Compile(generator.CreateTypeRepository(null, string.Empty))); + sut.Add(); + Assert.True(sut.TryGet(typeof(TestDto), out _)); + } - [Fact] - public void RepositoryWitTestDto_TryGet_ReturnsRegisteredType() - { - var generator = new TypeRepositoryGenerator(); - var sut = GetTypeRepository(Compile(generator.CreateTypeRepository(null, string.Empty))); - sut.Add(); - sut.TryGet(typeof(TestDto), out var result); - Assert.Equal(typeof(TestDtoWrapped), result); - } + [Fact] + public void RepositoryWitTestDto_TryGet_ReturnsRegisteredType() + { + var generator = new TypeRepositoryGenerator(); + var sut = GetTypeRepository(Compile(generator.CreateTypeRepository(null, string.Empty))); + sut.Add(); + sut.TryGet(typeof(TestDto), out var result); + Assert.Equal(typeof(TestDtoWrapped), result); + } - [Fact] - public void RepositoryWitTestDto_TryGet_ReturnsPatchOfUserType() - { - var generator = new TypeRepositoryGenerator(); - var sut = GetTypeRepository(Compile(generator.CreateTypeRepository(null, string.Empty))); - sut.Add(); - sut.TryGet(typeof(TestDto), out var result); - Assert.True(typeof(Patch).IsAssignableFrom(result)); - } + [Fact] + public void RepositoryWitTestDto_TryGet_ReturnsPatchOfUserType() + { + var generator = new TypeRepositoryGenerator(); + var sut = GetTypeRepository(Compile(generator.CreateTypeRepository(null, string.Empty))); + sut.Add(); + sut.TryGet(typeof(TestDto), out var result); + Assert.True(typeof(Patch).IsAssignableFrom(result)); + } - [Fact] - public void GeneratedTypesInRepository_TryGet_ReturnsRegisteredType() - { - var generator = new TypeRepositoryGenerator(); - var sut = GetTypeRepository(Compile(generator.CreateTypeRepository(new[] { (typeof(TestDto).FullName, typeof(TestDtoWrapped).FullName) }, string.Empty))); - sut.TryGet(typeof(TestDto), out var result); - Assert.Equal(typeof(TestDtoWrapped), result); - } + [Fact] + public void GeneratedTypesInRepository_TryGet_ReturnsRegisteredType() + { + var generator = new TypeRepositoryGenerator(); + var sut = GetTypeRepository(Compile(generator.CreateTypeRepository(new[] { (typeof(TestDto).FullName, typeof(TestDtoWrapped).FullName) }, string.Empty))); + sut.TryGet(typeof(TestDto), out var result); + Assert.Equal(typeof(TestDtoWrapped), result); + } - [Fact] - public void SameTypeRegisteredTwice_InstanceProperty_Throws() - { - var generator = new TypeRepositoryGenerator(); - var input = new[] { (typeof(TestDto).FullName, typeof(TestDtoWrapped).FullName), (typeof(TestDto).FullName, typeof(TestDtoWrapped).FullName) }; - Assert.Throws(() => GetTypeRepository(Compile(generator.CreateTypeRepository(input, string.Empty)))); - } + [Fact] + public void SameTypeRegisteredTwice_InstanceProperty_Throws() + { + var generator = new TypeRepositoryGenerator(); + var input = new[] { (typeof(TestDto).FullName, typeof(TestDtoWrapped).FullName), (typeof(TestDto).FullName, typeof(TestDtoWrapped).FullName) }; + Assert.Throws(() => GetTypeRepository(Compile(generator.CreateTypeRepository(input, string.Empty)))); + } - private Assembly Compile(string code) - { - return SourceBuilder.CompileToAssembly(code, new[] { MetadataReference.CreateFromFile(typeof(TestDto).Assembly.Location) }); - } + private Assembly Compile(string code) + { + return SourceBuilder.CompileToAssembly(code, new[] { MetadataReference.CreateFromFile(typeof(TestDto).Assembly.Location) }); + } - private ITypeRepository GetTypeRepository(Assembly assembly) - { - return assembly.GetType("LaDeak.JsonMergePatch.Generated.Safe.TypeRepository").GetProperty("Instance").GetValue(null) as ITypeRepository; - } + private ITypeRepository GetTypeRepository(Assembly assembly) + { + return assembly.GetType("LaDeak.JsonMergePatch.Generated.Safe.TypeRepository").GetProperty("Instance").GetValue(null) as ITypeRepository; } -} +} \ No newline at end of file diff --git a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/TypesWithCtorSerializationTests.cs b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/TypesWithCtorSerializationTests.cs index 78890eb..f8e3e5c 100644 --- a/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/TypesWithCtorSerializationTests.cs +++ b/test/JsonMergePatch.SourceGenerator.Abstractions.Tests/TypesWithCtorSerializationTests.cs @@ -100,8 +100,10 @@ public TypesWithCtorSerializationData() private readonly string _vanillaRecordTypesProperties = @" namespace TestCode { + [LaDeak.JsonMergePatch.Abstractions.Patchable] public record SubDto(System.Int32 NumberProp, System.DateTime? NullableDateTimeProperty, System.Double CamelCaseProperty); + [LaDeak.JsonMergePatch.Abstractions.Patchable] public record ParentDto(System.String ParentStringProperty, SubDto OtherDto, System.Collections.Generic.IEnumerable Values); public class Program diff --git a/test/JsonMergePatch.Tests/JsonMergePatch.AspNetCore.Tests.csproj b/test/JsonMergePatch.Tests/JsonMergePatch.AspNetCore.Tests.csproj index b8fd6f2..376025a 100644 --- a/test/JsonMergePatch.Tests/JsonMergePatch.AspNetCore.Tests.csproj +++ b/test/JsonMergePatch.Tests/JsonMergePatch.AspNetCore.Tests.csproj @@ -4,13 +4,14 @@ net8.0 false LaDeak.JsonMergePatch.AspNetCore.Tests + true - + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/test/JsonMergePatch.Tests/JsonMergePatchInputReaderTests.cs b/test/JsonMergePatch.Tests/JsonMergePatchInputReaderTests.cs index ac3c3e5..6fa1a2a 100644 --- a/test/JsonMergePatch.Tests/JsonMergePatchInputReaderTests.cs +++ b/test/JsonMergePatch.Tests/JsonMergePatchInputReaderTests.cs @@ -1,7 +1,4 @@ -using System; -using System.IO; -using System.Text; -using System.Threading.Tasks; +using System.Text; using LaDeak.JsonMergePatch.Abstractions; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Json; @@ -68,7 +65,7 @@ public async Task NoTypeRepositoryRegistration_ReadRequestBodyAsync_ReturnsFailu public async Task InvalidJson_ReadRequestBodyAsync_ReturnsFailure() { var sut = new JsonMergePatchInputReader(new JsonOptions(), CreateTypeRepository()); - DefaultHttpContext httpContext = await CreateHttpContextAsync("invalid").ConfigureAwait(false); + DefaultHttpContext httpContext = await CreateHttpContextAsync("invalid"); var inputContext = CreateInputFormatterContext(typeof(Patch), httpContext); var result = await sut.ReadRequestBodyAsync(inputContext, Encoding.UTF8); @@ -81,7 +78,7 @@ public async Task InvalidJson_ReadRequestBodyAsync_ReturnsFailure() public async Task ValidJson_ReadRequestBodyAsync_ReturnsFailure() { var sut = new JsonMergePatchInputReader(new JsonOptions(), CreateTypeRepository()); - DefaultHttpContext httpContext = await CreateHttpContextAsync("{ \"Prop1\" : 5 }").ConfigureAwait(false); + DefaultHttpContext httpContext = await CreateHttpContextAsync("{ \"Prop1\" : 5 }"); var inputContext = CreateInputFormatterContext(typeof(Patch), httpContext); var result = await sut.ReadRequestBodyAsync(inputContext, Encoding.UTF8); @@ -93,7 +90,7 @@ public async Task ValidJson_ReadRequestBodyAsync_ReturnsFailure() public async Task OtherEncodingJson_ReadRequestBodyAsync_ReturnsFailure() { var sut = new JsonMergePatchInputReader(new JsonOptions(), CreateTypeRepository()); - DefaultHttpContext httpContext = await CreateHttpContextAsync("{ \"Prop1\" : 5 }", Encoding.Unicode).ConfigureAwait(false); + DefaultHttpContext httpContext = await CreateHttpContextAsync("{ \"Prop1\" : 5 }", Encoding.Unicode); var inputContext = CreateInputFormatterContext(typeof(Patch), httpContext); var result = await sut.ReadRequestBodyAsync(inputContext, Encoding.Unicode); @@ -105,7 +102,7 @@ public async Task OtherEncodingJson_ReadRequestBodyAsync_ReturnsFailure() public async Task NoPatchInputType_ReadRequestBodyAsync_ReturnsFailure() { var sut = new JsonMergePatchInputReader(new JsonOptions(), CreateTypeRepository()); - DefaultHttpContext httpContext = await CreateHttpContextAsync("{ \"Prop1\" : 5 }").ConfigureAwait(false); + DefaultHttpContext httpContext = await CreateHttpContextAsync("{ \"Prop1\" : 5 }"); var inputContext = CreateInputFormatterContext(typeof(TestDto), httpContext); var result = await sut.ReadRequestBodyAsync(inputContext, Encoding.UTF8); @@ -117,7 +114,7 @@ public async Task NoPatchInputType_ReadRequestBodyAsync_ReturnsFailure() public async Task OpenPatchType_CanRead_ReturnsTrue() { var sut = new JsonMergePatchInputReader(new JsonOptions(), CreateTypeRepository()); - DefaultHttpContext httpContext = await CreateHttpContextAsync("{ \"Prop1\" : 5 }").ConfigureAwait(false); + DefaultHttpContext httpContext = await CreateHttpContextAsync("{ \"Prop1\" : 5 }"); var inputContext = CreateInputFormatterContext(typeof(Patch<>), httpContext); Assert.True(sut.CanRead(inputContext)); @@ -127,7 +124,7 @@ public async Task OpenPatchType_CanRead_ReturnsTrue() public async Task ClosedPatchType_CanRead_ReturnsTrue() { var sut = new JsonMergePatchInputReader(new JsonOptions(), CreateTypeRepository()); - DefaultHttpContext httpContext = await CreateHttpContextAsync("{ \"Prop1\" : 5 }").ConfigureAwait(false); + DefaultHttpContext httpContext = await CreateHttpContextAsync("{ \"Prop1\" : 5 }"); var inputContext = CreateInputFormatterContext(typeof(Patch), httpContext); Assert.True(sut.CanRead(inputContext)); @@ -137,7 +134,7 @@ public async Task ClosedPatchType_CanRead_ReturnsTrue() public async Task NonPatchType_CanRead_ReturnsFalse() { var sut = new JsonMergePatchInputReader(new JsonOptions(), CreateTypeRepository()); - DefaultHttpContext httpContext = await CreateHttpContextAsync("{ \"Prop1\" : 5 }").ConfigureAwait(false); + DefaultHttpContext httpContext = await CreateHttpContextAsync("{ \"Prop1\" : 5 }"); var inputContext = CreateInputFormatterContext(typeof(TestDto), httpContext); Assert.False(sut.CanRead(inputContext)); @@ -159,7 +156,7 @@ private async Task CreateHttpContextAsync(string requestBody var memoryStream = new MemoryStream(); using var writer = new StreamWriter(memoryStream, encoding: encoding ?? Encoding.UTF8, leaveOpen: true); - await writer.WriteAsync(requestBody).ConfigureAwait(false); + await writer.WriteAsync(requestBody); await writer.FlushAsync(); memoryStream.Seek(0, SeekOrigin.Begin); httpContext.Request.Body = memoryStream; diff --git a/test/JsonMergePatch.Tests/TestDto.cs b/test/JsonMergePatch.Tests/TestDto.cs index 823975d..3a0bf4b 100644 --- a/test/JsonMergePatch.Tests/TestDto.cs +++ b/test/JsonMergePatch.Tests/TestDto.cs @@ -1,7 +1,6 @@ -namespace LaDeak.JsonMergePatch.AspNetCore.Tests +namespace LaDeak.JsonMergePatch.AspNetCore.Tests; + +public class TestDto { - public class TestDto - { - public int Prop1 { get; set; } - } + public int Prop1 { get; set; } } diff --git a/test/JsonMergePatch.Tests/TestDtoWrapped.cs b/test/JsonMergePatch.Tests/TestDtoWrapped.cs index 1d93d44..b188344 100644 --- a/test/JsonMergePatch.Tests/TestDtoWrapped.cs +++ b/test/JsonMergePatch.Tests/TestDtoWrapped.cs @@ -1,23 +1,22 @@ using LaDeak.JsonMergePatch.Abstractions; -namespace LaDeak.JsonMergePatch.AspNetCore.Tests +namespace LaDeak.JsonMergePatch.AspNetCore.Tests; + +public class TestDtoWrapped : Patch { - public class TestDtoWrapped : Patch + public TestDtoWrapped() { - public TestDtoWrapped() - { - Properties = new bool[1]; - } + Properties = new bool[1]; + } - private int? _prop1; - public int? Prop1 { get => _prop1; set { Properties[0] = true; _prop1 = value; } } + private int? _prop1; + public int? Prop1 { get => _prop1; set { Properties[0] = true; _prop1 = value; } } - public override TestDto ApplyPatch(TestDto input) - { - input ??= new(); - if (Properties[0]) - input.Prop1 = Prop1.HasValue ? Prop1.Value : default; - return input; - } + public override TestDto ApplyPatch(TestDto input) + { + input ??= new(); + if (Properties[0]) + input.Prop1 = Prop1.HasValue ? Prop1.Value : default; + return input; } }