Skip to content

Commit

Permalink
Merge pull request #14 from ladeak/multi_library
Browse files Browse the repository at this point in the history
Multi library support
  • Loading branch information
ladeak authored Mar 2, 2021
2 parents 4536914 + 72e0a48 commit ed5e6ff
Show file tree
Hide file tree
Showing 25 changed files with 410 additions and 85 deletions.
7 changes: 7 additions & 0 deletions JsonMergePatch.sln
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonMergePatch.SourceGenera
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonMergePatch.SourceGenerator", "src\JsonMergePatch.SourceGenerator\JsonMergePatch.SourceGenerator.csproj", "{CEB6F7B7-15A4-485E-8372-7687852EB929}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleAppLibrary", "sample\ConsoleAppLibrary\ConsoleAppLibrary.csproj", "{3592FF83-D9D6-473A-97CE-94FF0D7FE5F1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -77,13 +79,18 @@ Global
{CEB6F7B7-15A4-485E-8372-7687852EB929}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CEB6F7B7-15A4-485E-8372-7687852EB929}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CEB6F7B7-15A4-485E-8372-7687852EB929}.Release|Any CPU.Build.0 = Release|Any CPU
{3592FF83-D9D6-473A-97CE-94FF0D7FE5F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3592FF83-D9D6-473A-97CE-94FF0D7FE5F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3592FF83-D9D6-473A-97CE-94FF0D7FE5F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3592FF83-D9D6-473A-97CE-94FF0D7FE5F1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{910E8728-872B-43EF-994F-B67953874BCB} = {0E750D74-69C7-433C-92C7-3F026083C255}
{1FE80A1A-9402-4953-9A7F-F01B1D0539B5} = {0E750D74-69C7-433C-92C7-3F026083C255}
{3592FF83-D9D6-473A-97CE-94FF0D7FE5F1} = {0E750D74-69C7-433C-92C7-3F026083C255}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {55007954-1D8A-4553-A15C-FE4AE17C0A4B}
Expand Down
16 changes: 16 additions & 0 deletions sample/AspNetCoreWebApi/Controllers/SampleController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ public class WeatherForecast
public string Summary { get; set; }
}

public class DeviceData
{
public double Watts { get; set; }
public string Name { get; set; }
}

[ApiController]
[Route("[controller]")]
public class SampleController : ControllerBase
Expand Down Expand Up @@ -54,6 +60,16 @@ public WeatherForecast GetWeather()
};
}

[HttpGet("DeviceData")]
public DeviceData GetDeviceData()
{
return new DeviceData
{
Name = "test device1",
Watts = 12
};
}

[HttpPatch("PatchWeather")]
public WeatherForecast PatchForecast(Patch<WeatherForecast> input)
{
Expand Down
6 changes: 1 addition & 5 deletions sample/ConsoleApp/ConsoleApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,10 @@
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<!--<PackageReference Include="LaDeak.JsonMergePatch.Http" Version="0.0.1-alpha1" />
<PackageReference Include="LaDeak.JsonMergePatch.SourceGenerator" Version="0.0.1-alpha1" />-->
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\JsonMergePatch.Http\JsonMergePatch.Http.csproj" />
<ProjectReference Include="..\..\src\JsonMergePatch.SourceGenerator\JsonMergePatch.SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\ConsoleAppLibrary\ConsoleAppLibrary.csproj" />
</ItemGroup>

</Project>
7 changes: 6 additions & 1 deletion sample/ConsoleApp/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using ConsoleAppLibrary;
using LaDeak.JsonMergePatch.Abstractions;
using LaDeak.JsonMergePatch.Http;

namespace ReadJsonPatchAsync
Expand All @@ -16,7 +18,7 @@ public class WeatherForecast

public static async Task Main(string[] args)
{
LaDeak.JsonMergePatch.Abstractions.JsonMergePatchOptions.Repository = LaDeak.JsonMergePatch.Generated.TypeRepository.Instance;
LaDeak.JsonMergePatch.Abstractions.JsonMergePatchOptions.Repository = LaDeak.JsonMergePatch.Generated.SafeConsoleApp.TypeRepository.Instance.Extend(LaDeak.JsonMergePatch.Generated.SafeConsoleAppLibrary.TypeRepository.Instance);
await ReadAsJsonMergePatchAsync();
}

Expand All @@ -28,6 +30,9 @@ public static async Task ReadAsJsonMergePatchAsync()
var original = new WeatherForecast() { Date = DateTime.UtcNow, Summary = "Sample weather forecast", Temp = 24 };
var result = responseData.ApplyPatch(original);
Console.WriteLine($"Patched: Date={result.Date}, Summary={result.Summary}, Temp={result.Temp}");

var client = new Client();
await client.ReadAsJsonMergePatchAsync();
}
}
}
12 changes: 12 additions & 0 deletions sample/ConsoleAppLibrary/ConsoleAppLibrary.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\JsonMergePatch.Http\JsonMergePatch.Http.csproj" />
<ProjectReference Include="..\..\src\JsonMergePatch.SourceGenerator\JsonMergePatch.SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>

</Project>
26 changes: 26 additions & 0 deletions sample/ConsoleAppLibrary/Sample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using LaDeak.JsonMergePatch.Http;

namespace ConsoleAppLibrary
{
public class DeviceData
{
public double Watts { get; set; }
public string Name { get; set; }
}

public class Client
{
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<DeviceData>().ConfigureAwait(false);
var original = new DeviceData() { Watts = 5, Name = "phone" };
var result = responseData.ApplyPatch(original);
Console.WriteLine($"Patched: Name={result.Name}, Watts={result.Watts}");
}
}
}
12 changes: 12 additions & 0 deletions src/JsonMergePatch.Abstractions/ITypeRepository.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace LaDeak.JsonMergePatch.Abstractions
Expand All @@ -19,5 +20,16 @@ public interface ITypeRepository
/// <param name="wrapper">Type that wraps the user type.</param>
/// <returns>True if the user type had a registration, otherwise false.</returns>
bool TryGet(Type source, [NotNullWhen(true)] out Type wrapper);

/// <summary>
/// Adds a type and corresponding wrapper to the type repository.
/// </summary>
void Add(Type source, Type wrapper);

/// <summary>
/// Returns all registrations.
/// </summary>
/// <returns>Az enumeration of registered types and corresponding wrapper types.</returns>
public IEnumerable<KeyValuePair<Type, Type>> GetAll();
}
}
20 changes: 20 additions & 0 deletions src/JsonMergePatch.Abstractions/ITypeRepositoryExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace LaDeak.JsonMergePatch.Abstractions
{
public static class ITypeRepositoryExtensions
{
public static ITypeRepository Extend(this ITypeRepository target, ITypeRepository source)
{
_ = target ?? throw new ArgumentNullException(nameof(target));
_ = source ?? throw new ArgumentNullException(nameof(source));

foreach (var item in source.GetAll() ?? Enumerable.Empty<KeyValuePair<Type, Type>>())
if (!target.TryGet(item.Key, out _))
target.Add(item.Key, item.Value);
return target;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ public class AspNetJsonMergePatchSourceGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
var currentAssemblyName = context.Compilation.Assembly.Name;
var namespaceName = NameBuilder.GetNamespace(currentAssemblyName);
var modelBuilderGenerator = new ModelBuilderExtensionGenerator();
var mvcExtension = modelBuilderGenerator.CreateModelBuilder("LaDeak.JsonMergePatch.Generated.TypeRepository.Instance");
var mvcExtension = modelBuilderGenerator.CreateModelBuilder($"{namespaceName}.TypeRepository.Instance");
context.AddSource("LaDeakJsonMergePatchModelBuilderExtension", SourceText.From(mvcExtension, Encoding.UTF8));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ namespace LaDeak.JsonMergePatch.Generated
{
public static class MvcBuilderExtensions
{
public static IMvcBuilder AddJsonMergePatch(this IMvcBuilder builder, JsonOptions jsonOptions = null)
public static IMvcBuilder AddJsonMergePatch(this IMvcBuilder builder, JsonOptions jsonOptions = null, LaDeak.JsonMergePatch.Abstractions.ITypeRepository typeRepository = null)
{");
if (!string.IsNullOrWhiteSpace(typeRepositoryAccessor))
{
sb.AppendLine($" builder.Services.AddSingleton<LaDeak.JsonMergePatch.Abstractions.ITypeRepository>({typeRepositoryAccessor});");
sb.AppendLine($" LaDeak.JsonMergePatch.Abstractions.JsonMergePatchOptions.Repository = {typeRepositoryAccessor};");
sb.AppendLine($" builder.Services.AddSingleton<LaDeak.JsonMergePatch.Abstractions.ITypeRepository>(typeRepository ?? {typeRepositoryAccessor});");
sb.AppendLine($" LaDeak.JsonMergePatch.Abstractions.JsonMergePatchOptions.Repository = typeRepository ?? {typeRepositoryAccessor};");
}
sb.Append(@" jsonOptions ??= new JsonOptions();
return builder.AddMvcOptions(options => options.InputFormatters.Insert(0, new LaDeak.JsonMergePatch.AspNetCore.JsonMergePatchInputReader(jsonOptions)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ protected virtual IEnumerable<GeneratedWrapper> ExecuteImpl(GeneratorExecutionCo
context.AddSource(generatedType.FileName, SourceText.From(generatedType.SourceCode, Encoding.UTF8));

var tyeRepoGenerator = new TypeRepositoryGenerator();
var typeRepoClass = tyeRepoGenerator.CreateTypeRepository(types.Select(x => (x.SourceTypeFullName, x.TargetTypeFullName)));
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;
}
Expand Down
19 changes: 19 additions & 0 deletions src/JsonMergePatch.SourceGenerator/NameBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Microsoft.CodeAnalysis;

namespace LaDeak.JsonMergePatch.SourceGenerator
{
public static class NameBuilder
{
private const string Namespace = "LaDeak.JsonMergePatch.Generated";

public static string GetName(ITypeSymbol typeInfo) => $"{typeInfo.Name}Wrapped";

public static string GetNamespaceExtension(ITypeSymbol typeInfo) => $"Safe{typeInfo.ContainingNamespace.ToDisplayString()}";

public static string GetFullTypeName(ITypeSymbol typeInfo) => $"{Namespace}.{GetNamespaceExtension(typeInfo)}.{GetName(typeInfo)}";

public static string GetNamespace(ITypeSymbol typeInfo) => $"{Namespace}.{GetNamespaceExtension(typeInfo)}";

public static string GetNamespace(string extension) => $"{Namespace}.Safe{extension}";
}
}
22 changes: 7 additions & 15 deletions src/JsonMergePatch.SourceGenerator/TypeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,22 @@ namespace LaDeak.JsonMergePatch.SourceGenerator
{
public class TypeBuilder : ITypeBuilder
{
private const string Namespace = "LaDeak.JsonMergePatch.Generated";

public GeneratedWrapper BuildWrapperType(ITypeSymbol typeInfo, string sourceTypeName)
{
var name = GetName(typeInfo);
var name = NameBuilder.GetName(typeInfo);
var state = InitializeState(typeInfo, name, sourceTypeName);
BuildFile(state);
return new GeneratedWrapper()
{
FileName = $"LaDeakJsonMergePatch{GetNamespaceExtension(typeInfo)}{name}",
FileName = $"LaDeakJsonMergePatch{NameBuilder.GetNamespaceExtension(typeInfo)}{name}",
SourceCode = state.Builder.ToString(),
SourceTypeFullName = sourceTypeName,
TargetTypeFullName = GetFullName(typeInfo),
TargetTypeFullName = NameBuilder.GetFullTypeName(typeInfo),
ToProcessTypes = state.ToProcessTypeSymbols
};
}

private string GetName(ITypeSymbol typeInfo) => $"{typeInfo.Name}Wrapped";

private string GetNamespaceExtension(ITypeSymbol typeInfo) => $"S{typeInfo.ContainingNamespace.ToDisplayString()}";

private string GetFullName(ITypeSymbol typeInfo) => $"{Namespace}.{GetNamespaceExtension(typeInfo)}.{GetName(typeInfo)}";

private void BuildFile(BuilderState state) => BuildNameSpace(state, BuildClass);
private void BuildFile(BuilderState state) => BuildNamespace(state, BuildClass);

private void BuildClass(BuilderState state) => BuildClassDeclaration(state, s => BuildClassBody(s));

Expand All @@ -49,9 +41,9 @@ private BuilderState InitializeState(ITypeSymbol typeInfo, string name, string s
return new BuilderState(typeInformation);
}

private void BuildNameSpace(BuilderState state, Action<BuilderState>? addBody = null)
private void BuildNamespace(BuilderState state, Action<BuilderState>? addBody = null)
{
state.AppendLine($"namespace {Namespace}.{GetNamespaceExtension(state.TypeInfo.TypeSymbol)}");
state.AppendLine($"namespace {NameBuilder.GetNamespace(state.TypeInfo.TypeSymbol)}");
state.AppendLine("{");
addBody?.Invoke(state.IncrementIdentation());
state.AppendLine("}");
Expand Down Expand Up @@ -142,7 +134,7 @@ private string ConvertToNullableIfRequired(PropertyInformation propertyInfo, ITy
{
if (GeneratedTypeFilter.IsGeneratableType(propertyTypeSymbol))
{
return (true, GetFullName(propertyTypeSymbol));
return (true, NameBuilder.GetFullTypeName(propertyTypeSymbol));
}
return (false, propertyTypeSymbol.ToDisplayString(GeneratedTypeFilter.SymbolFormat));
}
Expand Down
17 changes: 13 additions & 4 deletions src/JsonMergePatch.SourceGenerator/TypeRepositoryGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ namespace LaDeak.JsonMergePatch.SourceGenerator
{
public class TypeRepositoryGenerator
{
public string CreateTypeRepository(IEnumerable<(string, string)> typeRegistrations)
public string CreateTypeRepository(IEnumerable<(string, string)> typeRegistrations, string assemblyName)
{
StringBuilder sb = new StringBuilder(@"
namespace LaDeak.JsonMergePatch.Generated
StringBuilder sb = new StringBuilder(@$"
namespace {NameBuilder.GetNamespace(assemblyName)}");
sb.Append(@"
{
public class TypeRepository : LaDeak.JsonMergePatch.Abstractions.ITypeRepository
{
Expand All @@ -24,17 +25,25 @@ private TypeRepository()
sb.Append(@"
}
public static LaDeak.JsonMergePatch.Abstractions.ITypeRepository Instance { get; } = new TypeRepository();
public void Add<TSource, TWrapper>() where TWrapper : LaDeak.JsonMergePatch.Abstractions.Patch<TSource>
{
_repository.Add(typeof(TSource), typeof(TWrapper));
}
public void Add(System.Type source, System.Type wrapper)
{
if (wrapper.IsSubclassOf(typeof(LaDeak.JsonMergePatch.Abstractions.Patch<>).MakeGenericType(source)))
_repository.Add(source, wrapper);
}
public bool TryGet(System.Type source, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out System.Type wrapper)
{
return _repository.TryGetValue(source, out wrapper);
}
public static LaDeak.JsonMergePatch.Abstractions.ITypeRepository Instance { get; } = new TypeRepository();
public System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<System.Type, System.Type>> GetAll() => _repository;
}
}");
return sb.ToString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public void ApplyPatch_PropertyToDelete_SetsNullOnTarget(
var compilation = CreateInputOutputCompilation(sourceCode);
var outputAssembly = SourceBuilder.EmitToAssembly(compilation);

var parentDtoWrappedMetadata = outputAssembly.GetType("LaDeak.JsonMergePatch.Generated.STestCode.ParentDtoWrapped");
var parentDtoWrappedMetadata = outputAssembly.GetType("LaDeak.JsonMergePatch.Generated.SafeTestCode.ParentDtoWrapped");
var targetParentMetadata = outputAssembly.GetType("TestCode.ParentDto");
var targetSubMetadata = outputAssembly.GetType("TestCode.SubDto");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public void ApplyPatch_PropertyToDelete_SetsNullOnTarget(
var compilation = CreateWrappedTypeCompilation(source);
var outputAssembly = SourceBuilder.EmitToAssembly(compilation);

var dtoWrappedMetadata = outputAssembly.GetType("LaDeak.JsonMergePatch.Generated.STestCode.DtoWrapped");
var dtoWrappedMetadata = outputAssembly.GetType("LaDeak.JsonMergePatch.Generated.SafeTestCode.DtoWrapped");
var targetMetadata = outputAssembly.GetType("TestCode.Dto");
var target = targetMetadata.GetConstructor(new Type[0]).Invoke(null);
targetMetadata.GetProperty("Values").SetValue(target, input);
Expand Down
Loading

0 comments on commit ed5e6ff

Please sign in to comment.