Browse Source

Isolated new serialization logic, decoupled from json.

voice-allocs
RogueException 7 years ago
parent
commit
99d3283d25
100 changed files with 978 additions and 907 deletions
  1. +22
    -4
      Discord.Net.sln
  2. +6
    -1
      src/Discord.Net.Core/Discord.Net.Core.csproj
  3. +4
    -3
      src/Discord.Net.Rest/Discord.Net.Rest.csproj
  4. +16
    -4
      src/Discord.Net.Rest/DiscordRestApiClient.cs
  5. +4
    -4
      src/Discord.Net.Rest/DiscordRestClient.cs
  6. +0
    -132
      src/Discord.Net.Rest/Extensions/JsonReaderExtensions.cs
  7. +1
    -1
      src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs
  8. +0
    -34
      src/Discord.Net.Rest/Serialization/Converters/ArrayPropertyConverter.cs
  9. +0
    -103
      src/Discord.Net.Rest/Serialization/Converters/Converter.cs
  10. +0
    -32
      src/Discord.Net.Rest/Serialization/Converters/EntityOrIdPropertyConverter.cs
  11. +0
    -12
      src/Discord.Net.Rest/Serialization/Converters/EnumPropertyConverter.cs
  12. +0
    -10
      src/Discord.Net.Rest/Serialization/Converters/IPropertyConverter.cs
  13. +0
    -12
      src/Discord.Net.Rest/Serialization/Converters/ImagePropertyConverter.cs
  14. +0
    -32
      src/Discord.Net.Rest/Serialization/Converters/NullablePropertyConverter.cs
  15. +0
    -23
      src/Discord.Net.Rest/Serialization/Converters/OptionalPropertyConverter.cs
  16. +0
    -18
      src/Discord.Net.Rest/Serialization/Converters/Primitives/CharPropertyConverter.cs
  17. +0
    -19
      src/Discord.Net.Rest/Serialization/Converters/Primitives/DateTimeOffsetPropertyConverter.cs
  18. +0
    -19
      src/Discord.Net.Rest/Serialization/Converters/Primitives/DateTimePropertyConverter.cs
  19. +0
    -18
      src/Discord.Net.Rest/Serialization/Converters/Primitives/DecimalPropertyConverter.cs
  20. +0
    -18
      src/Discord.Net.Rest/Serialization/Converters/Primitives/DoublePropertyConverter.cs
  21. +0
    -18
      src/Discord.Net.Rest/Serialization/Converters/Primitives/Int16PropertyConverter.cs
  22. +0
    -18
      src/Discord.Net.Rest/Serialization/Converters/Primitives/Int32PropertyConverter.cs
  23. +0
    -18
      src/Discord.Net.Rest/Serialization/Converters/Primitives/Int64PropertyConverter.cs
  24. +0
    -18
      src/Discord.Net.Rest/Serialization/Converters/Primitives/Int8PropertyConverter.cs
  25. +0
    -18
      src/Discord.Net.Rest/Serialization/Converters/Primitives/SinglePropertyConverter.cs
  26. +0
    -18
      src/Discord.Net.Rest/Serialization/Converters/Primitives/StringPropertyConverter.cs
  27. +0
    -18
      src/Discord.Net.Rest/Serialization/Converters/Primitives/UInt16PropertyConverter.cs
  28. +0
    -18
      src/Discord.Net.Rest/Serialization/Converters/Primitives/UInt32PropertyConverter.cs
  29. +0
    -18
      src/Discord.Net.Rest/Serialization/Converters/Primitives/UInt64PropertyConverter.cs
  30. +0
    -18
      src/Discord.Net.Rest/Serialization/Converters/Primitives/UInt8PropertyConverter.cs
  31. +32
    -0
      src/Discord.Net.Rest/Serialization/JsonConverters/EntityOrIdPropertyConverter.cs
  32. +12
    -0
      src/Discord.Net.Rest/Serialization/JsonConverters/ImagePropertyConverter.cs
  33. +5
    -5
      src/Discord.Net.Rest/Serialization/JsonConverters/Int53PropertyConverter.cs
  34. +23
    -0
      src/Discord.Net.Rest/Serialization/JsonConverters/OptionalPropertyConverter.cs
  35. +5
    -5
      src/Discord.Net.Rest/Serialization/JsonConverters/UInt53PropertyConverter.cs
  36. +0
    -86
      src/Discord.Net.Rest/Serialization/ModelMap.cs
  37. +0
    -61
      src/Discord.Net.Rest/Serialization/PropertyMap.cs
  38. +0
    -35
      src/Discord.Net.Rest/Serialization/Serializer.cs
  39. +5
    -5
      src/Discord.Net.Rpc/DiscordRpcApiClient.cs
  40. +17
    -17
      src/Discord.Net.Rpc/DiscordRpcClient.cs
  41. +95
    -0
      src/Discord.Net.Serialization/ConverterCollection.cs
  42. +18
    -0
      src/Discord.Net.Serialization/Discord.Net.Serialization.csproj
  43. +111
    -0
      src/Discord.Net.Serialization/Extensions/BufferExtensions.cs
  44. +36
    -0
      src/Discord.Net.Serialization/Extensions/JsonReaderExtensions.cs
  45. +8
    -8
      src/Discord.Net.Serialization/Json/Converters/Collections.cs
  46. +32
    -0
      src/Discord.Net.Serialization/Json/Converters/Nullable.cs
  47. +4
    -4
      src/Discord.Net.Serialization/Json/Converters/Primitives.Bool.cs
  48. +33
    -0
      src/Discord.Net.Serialization/Json/Converters/Primitives.DateTime.cs
  49. +12
    -0
      src/Discord.Net.Serialization/Json/Converters/Primitives.Enum.cs
  50. +46
    -0
      src/Discord.Net.Serialization/Json/Converters/Primitives.Float.cs
  51. +60
    -0
      src/Discord.Net.Serialization/Json/Converters/Primitives.Signed.cs
  52. +32
    -0
      src/Discord.Net.Serialization/Json/Converters/Primitives.String.cs
  53. +60
    -0
      src/Discord.Net.Serialization/Json/Converters/Primitives.Unsigned.cs
  54. +10
    -0
      src/Discord.Net.Serialization/Json/IJsonPropertyConverter.cs
  55. +12
    -0
      src/Discord.Net.Serialization/Json/IJsonPropertyMap.cs
  56. +102
    -0
      src/Discord.Net.Serialization/Json/JsonFormat.cs
  57. +33
    -0
      src/Discord.Net.Serialization/Json/JsonPropertyMap.cs
  58. +18
    -0
      src/Discord.Net.Serialization/ModelMap.cs
  59. +1
    -1
      src/Discord.Net.Serialization/ModelPropertyAttribute.cs
  60. +18
    -0
      src/Discord.Net.Serialization/PropertyMap.cs
  61. +1
    -1
      src/Discord.Net.Serialization/SerializationException.cs
  62. +57
    -0
      src/Discord.Net.Serialization/SerializationFormat.cs
  63. +27
    -0
      src/Discord.Net.Serialization/Serializer.cs
  64. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/ExperimentalBufferExtensions.cs
  65. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Binary/System/Binary/BufferReader.cs
  66. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Binary/System/Binary/BufferWriter.cs
  67. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Binary/System/Binary/UnsafeUtilities.cs
  68. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Buffers/Buffer.cs
  69. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Buffers/BufferExtensions.cs
  70. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Buffers/BufferHandle.cs
  71. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Buffers/BufferPool.cs
  72. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Buffers/IOutput.cs
  73. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Buffers/IRetainable.cs
  74. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Buffers/OwnedBuffer.cs
  75. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Buffers/ReadOnlyBuffer.cs
  76. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Buffers/Transformation.cs
  77. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Internal/ManagedBufferPool.cs
  78. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Internal/OwnedArray.cs
  79. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Runtime/BufferDebuggerView.cs
  80. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Runtime/BufferPrimitivesThrowHelper.cs
  81. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Runtime/Contract.cs
  82. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Runtime/HashingHelper.cs
  83. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Runtime/PrimitiveAttribute.cs
  84. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Runtime/ReadOnlyBufferDebuggerView.cs
  85. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Collections.Sequences/System/Collections/Sequences/ArrayList.cs
  86. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Collections.Sequences/System/Collections/Sequences/ISequence.cs
  87. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Collections.Sequences/System/Collections/Sequences/Position.cs
  88. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Collections.Sequences/System/Collections/Sequences/ResizableArray.cs
  89. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Collections.Sequences/System/Collections/Sequences/SequenceEnumerator.cs
  90. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Text.Formatting/System/Text/Formatting/CompositeFormat.cs
  91. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Text.Formatting/System/Text/Formatting/Formatters/ArrayFormatter.cs
  92. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Text.Formatting/System/Text/Formatting/Formatters/OutputFormatter.cs
  93. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Text.Formatting/System/Text/Formatting/Formatters/SequenceFormatter.cs
  94. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Text.Formatting/System/Text/Formatting/Formatters/StreamFormatter.cs
  95. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Text.Formatting/System/Text/Formatting/Formatters/StringFormatter.cs
  96. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Text.Formatting/System/Text/Formatting/IOutputExtensions.cs
  97. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Text.Formatting/System/Text/Formatting/ITextOutput.cs
  98. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Text.Formatting/System/Text/Formatting/ITextOutputExtensions.cs
  99. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Text.Formatting/System/Text/Parsing/SequenceParser.cs
  100. +0
    -0
      src/Discord.Net.Serialization/_corefxlab/System.Text.Json/System/Text/Json/JsonConstants.cs

+ 22
- 4
Discord.Net.sln View File

@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.4
VisualStudioVersion = 15.0.26711.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Core", "src\Discord.Net.Core\Discord.Net.Core.csproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}"
EndProject
@@ -14,8 +14,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Commands", "src
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.WebSocket", "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj", "{688FD1D8-7F01-4539-B2E9-F473C5D699C7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Providers", "Providers", "{B0657AAE-DCC5-4FBF-8E5D-1FB578CF3012}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Providers.WS4Net", "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj", "{6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A}"
@@ -24,6 +22,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Tests", "test\D
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Webhook", "src\Discord.Net.Webhook\Discord.Net.Webhook.csproj", "{9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{1876A445-1C70-4F84-912B-4D3CEA21E0C3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Serialization", "src\Discord.Net.Serialization\Discord.Net.Serialization.csproj", "{AA3B67BE-767E-4230-9810-F7948B6AE689}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -130,6 +132,18 @@ Global
{9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}.Release|x64.Build.0 = Release|Any CPU
{9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}.Release|x86.ActiveCfg = Release|Any CPU
{9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}.Release|x86.Build.0 = Release|Any CPU
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Debug|x64.ActiveCfg = Debug|Any CPU
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Debug|x64.Build.0 = Debug|Any CPU
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Debug|x86.ActiveCfg = Debug|Any CPU
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Debug|x86.Build.0 = Debug|Any CPU
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Release|Any CPU.Build.0 = Release|Any CPU
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Release|x64.ActiveCfg = Release|Any CPU
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Release|x64.Build.0 = Release|Any CPU
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Release|x86.ActiveCfg = Release|Any CPU
{AA3B67BE-767E-4230-9810-F7948B6AE689}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -139,7 +153,11 @@ Global
{5688A353-121E-40A1-8BFA-B17B91FB48FB} = {288C363D-A636-4EAE-9AC1-4698B641B26E}
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A}
{688FD1D8-7F01-4539-B2E9-F473C5D699C7} = {288C363D-A636-4EAE-9AC1-4698B641B26E}
{6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7} = {B0657AAE-DCC5-4FBF-8E5D-1FB578CF3012}
{6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7} = {1876A445-1C70-4F84-912B-4D3CEA21E0C3}
{9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A}
{AA3B67BE-767E-4230-9810-F7948B6AE689} = {1876A445-1C70-4F84-912B-4D3CEA21E0C3}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9828C525-49C7-48F4-A9E7-94E223052DA2}
EndGlobalSection
EndGlobal

+ 6
- 1
src/Discord.Net.Core/Discord.Net.Core.csproj View File

@@ -8,6 +8,11 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Serialization\**" />
<EmbeddedResource Remove="Serialization\**" />
<None Remove="Serialization\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Buffers" Version="4.4.0-preview2-25405-01" />
<PackageReference Include="System.Collections.Immutable" Version="1.3.1" />
<PackageReference Include="System.Interactive.Async" Version="3.1.1" />
@@ -15,6 +20,6 @@
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.4.0-preview2-25405-01" />
</ItemGroup>
<ItemGroup>
<Folder Include="Serialization\" />
<ProjectReference Include="..\Discord.Net.Serialization\Discord.Net.Serialization.csproj" />
</ItemGroup>
</Project>

+ 4
- 3
src/Discord.Net.Rest/Discord.Net.Rest.csproj View File

@@ -9,11 +9,15 @@
</PropertyGroup>
<ItemGroup>
<Compile Remove="Serialization\System.Buffers\**" />
<Compile Remove="_corefxlab\**" />
<EmbeddedResource Remove="Serialization\System.Buffers\**" />
<EmbeddedResource Remove="_corefxlab\**" />
<None Remove="Serialization\System.Buffers\**" />
<None Remove="_corefxlab\**" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" />
<ProjectReference Include="..\Discord.Net.Serialization\Discord.Net.Serialization.csproj" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'net45' ">
<PackageReference Include="System.Net.Http" Version="4.3.2" /> <!-- https://github.com/dotnet/corefx/issues/19535 -->
@@ -21,7 +25,4 @@
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup>
<Folder Include="_corefxlab\" />
</ItemGroup>
</Project>

+ 16
- 4
src/Discord.Net.Rest/DiscordRestApiClient.cs View File

@@ -4,6 +4,8 @@ using Discord.Net;
using Discord.Net.Queue;
using Discord.Net.Rest;
using Discord.Serialization;
using Discord.Serialization.Json;
using Discord.Serialization.Json.Converters;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -11,6 +13,7 @@ using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Formatting;
@@ -21,13 +24,22 @@ namespace Discord.API
{
internal class DiscordRestApiClient : IDisposable
{
static DiscordRestApiClient()
{
SerializationFormat.Json.AddConverter<Image, ImagePropertyConverter>();
SerializationFormat.Json.AddConverter<long, Int53PropertyConverter>(info => info.GetCustomAttribute<Int53Attribute>() != null);
SerializationFormat.Json.AddConverter<ulong, UInt53PropertyConverter>(info => info.GetCustomAttribute<Int53Attribute>() != null);
SerializationFormat.Json.AddGenericConverter(typeof(EntityOrId<>), typeof(EntityOrIdPropertyConverter<>));
SerializationFormat.Json.AddGenericConverter(typeof(Optional<>), typeof(OptionalPropertyConverter<>));
}

private static readonly ConcurrentDictionary<string, Func<BucketIds, string>> _bucketIdGenerators = new ConcurrentDictionary<string, Func<BucketIds, string>>();

public event Func<string, string, double, Task> SentRequest { add { _sentRequestEvent.Add(value); } remove { _sentRequestEvent.Remove(value); } }
private readonly AsyncEvent<Func<string, string, double, Task>> _sentRequestEvent = new AsyncEvent<Func<string, string, double, Task>>();
protected readonly SemaphoreSlim _stateLock;
protected readonly ScopedSerializer _serializer;
protected readonly Serializer _serializer;
protected readonly ConcurrentQueue<ArrayFormatter> _formatters;
private readonly RestClientProvider _restClientProvider;

@@ -44,7 +56,7 @@ namespace Discord.API
internal IRestClient RestClient { get; private set; }
internal ulong? CurrentUserId { get; set;}

public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, ScopedSerializer serializer, RetryMode defaultRetryMode = RetryMode.AlwaysRetry)
public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, Serializer serializer, RetryMode defaultRetryMode = RetryMode.AlwaysRetry)
{
_restClientProvider = restClientProvider;
UserAgent = userAgent;
@@ -1167,13 +1179,13 @@ namespace Discord.API
protected static double ToMilliseconds(Stopwatch stopwatch) => Math.Round((double)stopwatch.ElapsedTicks / (double)Stopwatch.Frequency * 1000.0, 2);
protected ReadOnlyBuffer<byte> SerializeJson(ArrayFormatter data, object value)
{
_serializer.WriteJson(data, value);
_serializer.Write(data, value);
return new ReadOnlyBuffer<byte>(data.Formatted.Array, 0, data.Formatted.Count);
}
protected T DeserializeJson<T>(ReadOnlyBuffer<byte> data)
where T : class, new()
{
return _serializer.ReadJson<T>(data);
return _serializer.Read<T>(data);
}

internal class BucketIds


+ 4
- 4
src/Discord.Net.Rest/DiscordRestClient.cs View File

@@ -8,7 +8,7 @@ namespace Discord.Rest
{
public class DiscordRestClient : BaseDiscordClient, IDiscordClient
{
private readonly ScopedSerializer _serializer;
private readonly Serializer _serializer;
private RestApplication _applicationInfo;

public new RestSelfUser CurrentUser => base.CurrentUser as RestSelfUser;
@@ -16,10 +16,10 @@ namespace Discord.Rest
public DiscordRestClient() : this(new DiscordRestConfig()) { }
public DiscordRestClient(DiscordRestConfig config) : base(config)
{
_serializer = Serializer.CreateScope();
_serializer.Error += async ex =>
_serializer = new Serializer(SerializationFormat.Json);
_serializer.Error += ex =>
{
await _restLogger.WarningAsync("Serializer Error", ex);
_restLogger.WarningAsync("Serializer Error", ex).GetAwaiter().GetResult();
};

SetApiClient(new API.DiscordRestApiClient(config.RestClientProvider, DiscordConfig.UserAgent, _serializer, config.DefaultRetryMode));


+ 0
- 132
src/Discord.Net.Rest/Extensions/JsonReaderExtensions.cs View File

@@ -1,132 +0,0 @@
using System;
using System.Text;
using System.Text.Json;
using System.Text.Utf8;

namespace Discord.Serialization
{
internal static class JsonReaderExtensions
{
public static bool GetBool(this JsonReader reader) => GetBool(reader.Value);
public static bool GetBool(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseBoolean(text, out bool result, out int ignored, SymbolTable.InvariantUtf8))
return result;
throw new SerializationException("Failed to parse Boolean");
}

public static sbyte GetInt8(this JsonReader reader) => GetInt8(reader.Value);
public static sbyte GetInt8(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseSByte(text, out sbyte result, out int ignored, JsonConstants.NumberFormat, SymbolTable.InvariantUtf8))
return result;
throw new SerializationException("Failed to parse Int8");
}
public static short GetInt16(this JsonReader reader) => GetInt16(reader.Value);
public static short GetInt16(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseInt16(text, out short result, out int ignored, JsonConstants.NumberFormat, SymbolTable.InvariantUtf8))
return result;
throw new SerializationException("Failed to parse Int16");
}
public static int GetInt32(this JsonReader reader) => GetInt32(reader.Value);
public static int GetInt32(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseInt32(text, out int result, out int ignored, JsonConstants.NumberFormat, SymbolTable.InvariantUtf8))
return result;
throw new SerializationException("Failed to parse Int32");
}
public static long GetInt64(this JsonReader reader) => GetInt64(reader.Value);
public static long GetInt64(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseInt64(text, out long result, out int ignored, JsonConstants.NumberFormat, SymbolTable.InvariantUtf8))
return result;
throw new SerializationException("Failed to parse Int64");
}

public static byte GetUInt8(this JsonReader reader) => GetUInt8(reader.Value);
public static byte GetUInt8(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseByte(text, out byte result, out int ignored, JsonConstants.NumberFormat, SymbolTable.InvariantUtf8))
return result;
throw new SerializationException("Failed to parse UInt8");
}
public static ushort GetUInt16(this JsonReader reader) => GetUInt16(reader.Value);
public static ushort GetUInt16(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseUInt16(text, out ushort result, out int ignored, JsonConstants.NumberFormat, SymbolTable.InvariantUtf8))
return result;
throw new SerializationException("Failed to parse UInt16");
}
public static uint GetUInt32(this JsonReader reader) => GetUInt32(reader.Value);
public static uint GetUInt32(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseUInt32(text, out uint result, out int ignored, JsonConstants.NumberFormat, SymbolTable.InvariantUtf8))
return result;
throw new SerializationException("Failed to parse UInt32");
}
public static ulong GetUInt64(this JsonReader reader) => GetUInt64(reader.Value);
public static ulong GetUInt64(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseUInt64(text, out ulong result, out int ignored, JsonConstants.NumberFormat, SymbolTable.InvariantUtf8))
return result;
throw new SerializationException("Failed to parse UInt64");
}

public static char GetChar(this JsonReader reader) => GetChar(reader.Value);
public static char GetChar(this ReadOnlySpan<byte> text)
{
string str = GetString(text);
if (str.Length == 1)
return str[0];
throw new SerializationException("Failed to parse Char");
}
public static string GetString(this JsonReader reader) => GetString(reader.Value);
public static string GetString(this ReadOnlySpan<byte> text) => new Utf8String(text).ToString();

public static float GetSingle(this JsonReader reader) => GetSingle(reader.Value);
public static float GetSingle(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseDecimal(text, out decimal result, out int ignored, SymbolTable.InvariantUtf8))
return (float)result;
throw new SerializationException("Failed to parse Single");
}
public static double GetDouble(this JsonReader reader) => GetDouble(reader.Value);
public static double GetDouble(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseDecimal(text, out decimal result, out int ignored, SymbolTable.InvariantUtf8))
return (double)result;
throw new SerializationException("Failed to parse Double");
}
public static decimal GetDecimal(this JsonReader reader) => GetDecimal(reader.Value);
public static decimal GetDecimal(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseDecimal(text, out decimal result, out int ignored, SymbolTable.InvariantUtf8))
return result;
throw new SerializationException("Failed to parse Decimal");
}

public static DateTime GetDateTime(this JsonReader reader) => GetDateTime(reader.Value);
public static DateTime GetDateTime(this ReadOnlySpan<byte> text)
{
string str = GetString(text);
if (DateTime.TryParse(str, out var result)) //TODO: Improve perf
return result;
throw new SerializationException("Failed to parse DateTime");
}
public static DateTimeOffset GetDateTimeOffset(this JsonReader reader) => GetDateTimeOffset(reader.Value);
public static DateTimeOffset GetDateTimeOffset(this ReadOnlySpan<byte> text)
{
string str = GetString(text);
if (DateTimeOffset.TryParse(str, out var result)) //TODO: Improve perf
return result;
throw new SerializationException("Failed to parse DateTimeOffset");
}

public static void Skip(this JsonReader reader)
{
int initialDepth = reader._depth;
while (reader.Read() && reader._depth > initialDepth) { }
}
}
}

+ 1
- 1
src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs View File

@@ -104,7 +104,7 @@ namespace Discord.Net.Queue
{
try
{
var error = Serializer.ReadJson<Error>(response.Data);
var error = Serializer.Json.Read<Error>(response.Data);
code = error.Code;
reason = error.Message;
}


+ 0
- 34
src/Discord.Net.Rest/Serialization/Converters/ArrayPropertyConverter.cs View File

@@ -1,34 +0,0 @@
using System.Collections.Generic;
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal class ArrayPropertyConverter<T> : IPropertyConverter<T[]>
{
private readonly IPropertyConverter<T> _innerConverter;

public ArrayPropertyConverter(IPropertyConverter<T> innerConverter)
{
_innerConverter = innerConverter;
}

public T[] ReadJson(JsonReader reader, bool read = true)
{
if ((read && !reader.Read()) || reader.TokenType != JsonTokenType.StartArray)
throw new SerializationException("Bad input, expected StartArray");

var list = new List<T>();
while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
list.Add(_innerConverter.ReadJson(reader));
return list.ToArray();
}

public void WriteJson(JsonWriter writer, T[] value)
{
writer.WriteArrayStart();
for (int i = 0; i < value.Length; i++)
_innerConverter.WriteJson(writer, value[i]);
writer.WriteArrayEnd();
}
}
}

+ 0
- 103
src/Discord.Net.Rest/Serialization/Converters/Converter.cs View File

@@ -1,103 +0,0 @@
using Discord.API;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace Discord.Serialization.Converters
{
internal static class Converter
{
private static readonly MethodInfo _makeListConverterFunc
= typeof(Converter).GetTypeInfo().GetDeclaredMethod(nameof(MakeListConverterInternal));
private static readonly MethodInfo _makeOptionalConverterFunc
= typeof(Converter).GetTypeInfo().GetDeclaredMethod(nameof(MakeOptionalConverterInternal));
private static readonly MethodInfo _makeNullableConverterFunc
= typeof(Converter).GetTypeInfo().GetDeclaredMethod(nameof(MakeNullableConverterInternal));
private static readonly MethodInfo _makeEntityOrIdConverterFunc
= typeof(Converter).GetTypeInfo().GetDeclaredMethod(nameof(MakeEntityOrIdConverterInternal));

public static IPropertyConverter<TProp> For<TProp>()
=> (IPropertyConverter<TProp>)ForInternal<TProp>();
private static object ForInternal<TProp>()
{
var typeInfo = typeof(TProp).GetTypeInfo();

//Generics
if (typeof(TProp).IsConstructedGenericType)
{
Type genericType = typeof(TProp).GetGenericTypeDefinition();
if (genericType == typeof(List<>))
return MakeListConverter<TProp>(typeof(TProp).GenericTypeArguments[0]);
else if (genericType == typeof(Optional<>))
return MakeOptionalConverter<TProp>(typeof(TProp).GenericTypeArguments[0]);
else if (genericType == typeof(Nullable<>))
return MakeNullableConverter<TProp>(typeof(TProp).GenericTypeArguments[0]);
else if (genericType == typeof(EntityOrId<>))
return MakeEntityOrIdConverter<TProp>(typeof(TProp).GenericTypeArguments[0]);
}

//Enums
if (typeInfo.IsEnum) return new EnumPropertyConverter<TProp>();

//Primitives
if (typeof(TProp) == typeof(bool)) return new BooleanPropertyConverter();

if (typeof(TProp) == typeof(sbyte)) return new Int8PropertyConverter();
if (typeof(TProp) == typeof(short)) return new Int16PropertyConverter();
if (typeof(TProp) == typeof(int)) return new Int32PropertyConverter();
if (typeof(TProp) == typeof(long))
{
if (typeInfo.GetCustomAttribute<Int53Attribute>() != null)
return new Int53PropertyConverter();
else
return new Int64PropertyConverter();
}

if (typeof(TProp) == typeof(byte)) return new UInt8PropertyConverter();
if (typeof(TProp) == typeof(ushort)) return new UInt16PropertyConverter();
if (typeof(TProp) == typeof(uint)) return new UInt32PropertyConverter();
if (typeof(TProp) == typeof(ulong))
{
if (typeInfo.GetCustomAttribute<Int53Attribute>() != null)
return new UInt53PropertyConverter();
else
return new UInt64PropertyConverter();
}

if (typeof(TProp) == typeof(float)) return new SinglePropertyConverter();
if (typeof(TProp) == typeof(double)) return new DoublePropertyConverter();
if (typeof(TProp) == typeof(decimal)) return new DecimalPropertyConverter();

if (typeof(TProp) == typeof(char)) return new CharPropertyConverter();
if (typeof(TProp) == typeof(string)) return new StringPropertyConverter();

//Structs
if (typeof(TProp) == typeof(DateTime)) return new DateTimePropertyConverter();
if (typeof(TProp) == typeof(DateTimeOffset)) return new DateTimeOffsetPropertyConverter();
if (typeof(TProp) == typeof(Image)) return new ImagePropertyConverter();

throw new InvalidOperationException($"Unsupported model type: {typeof(TProp).Name}");
}

private static IPropertyConverter<TProp> MakeListConverter<TProp>(Type innerType)
=> _makeListConverterFunc.MakeGenericMethod(innerType).Invoke(null, null) as IPropertyConverter<TProp>;
private static IPropertyConverter<List<TInnerProp>> MakeListConverterInternal<TInnerProp>()
=> new ListPropertyConverter<TInnerProp>(For<TInnerProp>());

private static IPropertyConverter<TProp> MakeOptionalConverter<TProp>(Type innerType)
=> _makeOptionalConverterFunc.MakeGenericMethod(innerType).Invoke(null, null) as IPropertyConverter<TProp>;
private static IPropertyConverter<Optional<TInnerProp>> MakeOptionalConverterInternal<TInnerProp>()
=> new OptionalPropertyConverter<TInnerProp>(For<TInnerProp>());

private static IPropertyConverter<TProp> MakeNullableConverter<TProp>(Type innerType)
=> _makeNullableConverterFunc.MakeGenericMethod(innerType).Invoke(null, null) as IPropertyConverter<TProp>;
private static IPropertyConverter<TInnerProp?> MakeNullableConverterInternal<TInnerProp>()
where TInnerProp : struct
=> new NullablePropertyConverter<TInnerProp>(For<TInnerProp>());

private static IPropertyConverter<TProp> MakeEntityOrIdConverter<TProp>(Type innerType)
=> _makeEntityOrIdConverterFunc.MakeGenericMethod(innerType).Invoke(null, null) as IPropertyConverter<TProp>;
private static IPropertyConverter<EntityOrId<TInnerProp>> MakeEntityOrIdConverterInternal<TInnerProp>()
=> new EntityOrIdPropertyConverter<TInnerProp>(For<TInnerProp>());
}
}

+ 0
- 32
src/Discord.Net.Rest/Serialization/Converters/EntityOrIdPropertyConverter.cs View File

@@ -1,32 +0,0 @@
using Discord.API;
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal class EntityOrIdPropertyConverter<T> : IPropertyConverter<EntityOrId<T>>
{
private readonly IPropertyConverter<T> _innerConverter;

public EntityOrIdPropertyConverter(IPropertyConverter<T> innerConverter)
{
_innerConverter = innerConverter;
}

public EntityOrId<T> ReadJson(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType == JsonValueType.Number)
return new EntityOrId<T>(reader.GetUInt64());
return new EntityOrId<T>(_innerConverter.ReadJson(reader));
}

public void WriteJson(JsonWriter writer, EntityOrId<T> value)
{
if (value.Object != null)
_innerConverter.WriteJson(writer, value.Object);
else
writer.WriteValue(value.Id);
}
}
}

+ 0
- 12
src/Discord.Net.Rest/Serialization/Converters/EnumPropertyConverter.cs View File

@@ -1,12 +0,0 @@
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal class EnumPropertyConverter<T> : IPropertyConverter<T>
{
public T ReadJson(JsonReader reader, bool read = true)
=> throw new System.NotImplementedException();
public void WriteJson(JsonWriter writer, T value)
=> throw new System.NotImplementedException();
}
}

+ 0
- 10
src/Discord.Net.Rest/Serialization/Converters/IPropertyConverter.cs View File

@@ -1,10 +0,0 @@
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal interface IPropertyConverter<T>
{
T ReadJson(JsonReader reader, bool read = true);
void WriteJson(JsonWriter writer, T value);
}
}

+ 0
- 12
src/Discord.Net.Rest/Serialization/Converters/ImagePropertyConverter.cs View File

@@ -1,12 +0,0 @@
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal class ImagePropertyConverter : IPropertyConverter<Image>
{
public Image ReadJson(JsonReader reader, bool read = true)
=> throw new System.NotImplementedException();
public void WriteJson(JsonWriter writer, Image value)
=> throw new System.NotImplementedException();
}
}

+ 0
- 32
src/Discord.Net.Rest/Serialization/Converters/NullablePropertyConverter.cs View File

@@ -1,32 +0,0 @@
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal class NullablePropertyConverter<T> : IPropertyConverter<T?>
where T : struct
{
private readonly IPropertyConverter<T> _innerConverter;

public NullablePropertyConverter(IPropertyConverter<T> innerConverter)
{
_innerConverter = innerConverter;
}

public T? ReadJson(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType == JsonValueType.Null)
return null;
return _innerConverter.ReadJson(reader);
}

public void WriteJson(JsonWriter writer, T? value)
{
if (value.HasValue)
_innerConverter.WriteJson(writer, value.Value);
else
writer.WriteNull();
}
}
}

+ 0
- 23
src/Discord.Net.Rest/Serialization/Converters/OptionalPropertyConverter.cs View File

@@ -1,23 +0,0 @@
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal class OptionalPropertyConverter<T> : IPropertyConverter<Optional<T>>
{
private readonly IPropertyConverter<T> _innerConverter;

public OptionalPropertyConverter(IPropertyConverter<T> innerConverter)
{
_innerConverter = innerConverter;
}

public Optional<T> ReadJson(JsonReader reader, bool read = true)
=> new Optional<T>(_innerConverter.ReadJson(reader, read));

public void WriteJson(JsonWriter writer, Optional<T> value)
{
if (value.IsSpecified)
_innerConverter.WriteJson(writer, value.Value);
}
}
}

+ 0
- 18
src/Discord.Net.Rest/Serialization/Converters/Primitives/CharPropertyConverter.cs View File

@@ -1,18 +0,0 @@
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal class CharPropertyConverter : IPropertyConverter<char>
{
public char ReadJson(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.String)
throw new SerializationException("Bad input, expected String");
return reader.GetChar();
}
public void WriteJson(JsonWriter writer, char value)
=> writer.WriteValue(value);
}
}

+ 0
- 19
src/Discord.Net.Rest/Serialization/Converters/Primitives/DateTimeOffsetPropertyConverter.cs View File

@@ -1,19 +0,0 @@
using System;
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal class DateTimeOffsetPropertyConverter : IPropertyConverter<DateTimeOffset>
{
public DateTimeOffset ReadJson(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.String)
throw new SerializationException("Bad input, expected String");
return reader.GetDateTimeOffset();
}
public void WriteJson(JsonWriter writer, DateTimeOffset value)
=> writer.WriteValue(value);
}
}

+ 0
- 19
src/Discord.Net.Rest/Serialization/Converters/Primitives/DateTimePropertyConverter.cs View File

@@ -1,19 +0,0 @@
using System;
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal class DateTimePropertyConverter : IPropertyConverter<DateTime>
{
public DateTime ReadJson(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.String)
throw new SerializationException("Bad input, expected String");
return reader.GetDateTime();
}
public void WriteJson(JsonWriter writer, DateTime value)
=> writer.WriteValue(value);
}
}

+ 0
- 18
src/Discord.Net.Rest/Serialization/Converters/Primitives/DecimalPropertyConverter.cs View File

@@ -1,18 +0,0 @@
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal class DecimalPropertyConverter : IPropertyConverter<decimal>
{
public decimal ReadJson(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.Number)
throw new SerializationException("Bad input, expected Number");
return reader.GetDecimal();
}
public void WriteJson(JsonWriter writer, decimal value)
=> writer.WriteValue(value.ToString());
}
}

+ 0
- 18
src/Discord.Net.Rest/Serialization/Converters/Primitives/DoublePropertyConverter.cs View File

@@ -1,18 +0,0 @@
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal class DoublePropertyConverter : IPropertyConverter<double>
{
public double ReadJson(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.Number)
throw new SerializationException("Bad input, expected Number");
return reader.GetDouble();
}
public void WriteJson(JsonWriter writer, double value)
=> writer.WriteValue(value.ToString());
}
}

+ 0
- 18
src/Discord.Net.Rest/Serialization/Converters/Primitives/Int16PropertyConverter.cs View File

@@ -1,18 +0,0 @@
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal class Int16PropertyConverter : IPropertyConverter<short>
{
public short ReadJson(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.Number)
throw new SerializationException("Bad input, expected Number");
return reader.GetInt16();
}
public void WriteJson(JsonWriter writer, short value)
=> writer.WriteValue(value);
}
}

+ 0
- 18
src/Discord.Net.Rest/Serialization/Converters/Primitives/Int32PropertyConverter.cs View File

@@ -1,18 +0,0 @@
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal class Int32PropertyConverter : IPropertyConverter<int>
{
public int ReadJson(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.Number)
throw new SerializationException("Bad input, expected Number");
return reader.GetInt32();
}
public void WriteJson(JsonWriter writer, int value)
=> writer.WriteValue(value);
}
}

+ 0
- 18
src/Discord.Net.Rest/Serialization/Converters/Primitives/Int64PropertyConverter.cs View File

@@ -1,18 +0,0 @@
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal class Int64PropertyConverter : IPropertyConverter<long>
{
public long ReadJson(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.String)
throw new SerializationException("Bad input, expected String");
return reader.GetInt64();
}
public void WriteJson(JsonWriter writer, long value)
=> writer.WriteValue(value.ToString());
}
}

+ 0
- 18
src/Discord.Net.Rest/Serialization/Converters/Primitives/Int8PropertyConverter.cs View File

@@ -1,18 +0,0 @@
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal class Int8PropertyConverter : IPropertyConverter<sbyte>
{
public sbyte ReadJson(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.Number)
throw new SerializationException("Bad input, expected Number");
return reader.GetInt8();
}
public void WriteJson(JsonWriter writer, sbyte value)
=> writer.WriteValue(value);
}
}

+ 0
- 18
src/Discord.Net.Rest/Serialization/Converters/Primitives/SinglePropertyConverter.cs View File

@@ -1,18 +0,0 @@
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal class SinglePropertyConverter : IPropertyConverter<float>
{
public float ReadJson(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.Number)
throw new SerializationException("Bad input, expected Number");
return reader.GetSingle();
}
public void WriteJson(JsonWriter writer, float value)
=> writer.WriteValue(value.ToString());
}
}

+ 0
- 18
src/Discord.Net.Rest/Serialization/Converters/Primitives/StringPropertyConverter.cs View File

@@ -1,18 +0,0 @@
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal class StringPropertyConverter : IPropertyConverter<string>
{
public string ReadJson(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.String)
throw new SerializationException("Bad input, expected String");
return reader.GetString();
}
public void WriteJson(JsonWriter writer, string value)
=> writer.WriteValue(value);
}
}

+ 0
- 18
src/Discord.Net.Rest/Serialization/Converters/Primitives/UInt16PropertyConverter.cs View File

@@ -1,18 +0,0 @@
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal class UInt16PropertyConverter : IPropertyConverter<ushort>
{
public ushort ReadJson(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.Number)
throw new SerializationException("Bad input, expected Number");
return reader.GetUInt16();
}
public void WriteJson(JsonWriter writer, ushort value)
=> writer.WriteValue(value);
}
}

+ 0
- 18
src/Discord.Net.Rest/Serialization/Converters/Primitives/UInt32PropertyConverter.cs View File

@@ -1,18 +0,0 @@
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal class UInt32PropertyConverter : IPropertyConverter<uint>
{
public uint ReadJson(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.Number)
throw new SerializationException("Bad input, expected Number");
return reader.GetUInt32();
}
public void WriteJson(JsonWriter writer, uint value)
=> writer.WriteValue(value);
}
}

+ 0
- 18
src/Discord.Net.Rest/Serialization/Converters/Primitives/UInt64PropertyConverter.cs View File

@@ -1,18 +0,0 @@
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal class UInt64PropertyConverter : IPropertyConverter<ulong>
{
public ulong ReadJson(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.String)
throw new SerializationException("Bad input, expected String");
return reader.GetUInt64();
}
public void WriteJson(JsonWriter writer, ulong value)
=> writer.WriteValue(value.ToString());
}
}

+ 0
- 18
src/Discord.Net.Rest/Serialization/Converters/Primitives/UInt8PropertyConverter.cs View File

@@ -1,18 +0,0 @@
using System.Text.Json;

namespace Discord.Serialization.Converters
{
internal class UInt8PropertyConverter : IPropertyConverter<byte>
{
public byte ReadJson(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.Number)
throw new SerializationException("Bad input, expected Number");
return reader.GetUInt8();
}
public void WriteJson(JsonWriter writer, byte value)
=> writer.WriteValue(value);
}
}

+ 32
- 0
src/Discord.Net.Rest/Serialization/JsonConverters/EntityOrIdPropertyConverter.cs View File

@@ -0,0 +1,32 @@
using Discord.API;
using System.Text.Json;

namespace Discord.Serialization.Json.Converters
{
internal class EntityOrIdPropertyConverter<T> : IJsonPropertyConverter<EntityOrId<T>>
{
private readonly IJsonPropertyConverter<T> _innerConverter;

public EntityOrIdPropertyConverter(IJsonPropertyConverter<T> innerConverter)
{
_innerConverter = innerConverter;
}

public EntityOrId<T> Read(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType == JsonValueType.Number)
return new EntityOrId<T>(reader.ParseUInt64());
return new EntityOrId<T>(_innerConverter.Read(reader));
}

public void Write(JsonWriter writer, EntityOrId<T> value)
{
if (value.Object != null)
_innerConverter.Write(writer, value.Object);
else
writer.WriteValue(value.Id);
}
}
}

+ 12
- 0
src/Discord.Net.Rest/Serialization/JsonConverters/ImagePropertyConverter.cs View File

@@ -0,0 +1,12 @@
using System.Text.Json;

namespace Discord.Serialization.Json.Converters
{
internal class ImagePropertyConverter : IJsonPropertyConverter<API.Image>
{
public API.Image Read(JsonReader reader, bool read = true)
=> throw new System.NotImplementedException();
public void Write(JsonWriter writer, API.Image value)
=> throw new System.NotImplementedException();
}
}

src/Discord.Net.Rest/Serialization/Converters/Primitives/Int53PropertyConverter.cs → src/Discord.Net.Rest/Serialization/JsonConverters/Int53PropertyConverter.cs View File

@@ -1,18 +1,18 @@
using System.Text.Json;

namespace Discord.Serialization.Converters
namespace Discord.Serialization.Json.Converters
{
internal class Int53PropertyConverter : IPropertyConverter<long>
internal class Int53PropertyConverter : IJsonPropertyConverter<long>
{
public long ReadJson(JsonReader reader, bool read = true)
public long Read(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.Number)
throw new SerializationException("Bad input, expected Number");
return reader.GetInt64();
return reader.ParseInt64();
}
public void WriteJson(JsonWriter writer, long value)
public void Write(JsonWriter writer, long value)
=> writer.WriteValue(value);
}
}

+ 23
- 0
src/Discord.Net.Rest/Serialization/JsonConverters/OptionalPropertyConverter.cs View File

@@ -0,0 +1,23 @@
using System.Text.Json;

namespace Discord.Serialization.Json.Converters
{
internal class OptionalPropertyConverter<T> : IJsonPropertyConverter<Optional<T>>
{
private readonly IJsonPropertyConverter<T> _innerConverter;

public OptionalPropertyConverter(IJsonPropertyConverter<T> innerConverter)
{
_innerConverter = innerConverter;
}

public Optional<T> Read(JsonReader reader, bool read = true)
=> new Optional<T>(_innerConverter.Read(reader, read));

public void Write(JsonWriter writer, Optional<T> value)
{
if (value.IsSpecified)
_innerConverter.Write(writer, value.Value);
}
}
}

src/Discord.Net.Rest/Serialization/Converters/Primitives/UInt53PropertyConverter.cs → src/Discord.Net.Rest/Serialization/JsonConverters/UInt53PropertyConverter.cs View File

@@ -1,18 +1,18 @@
using System.Text.Json;

namespace Discord.Serialization.Converters
namespace Discord.Serialization.Json.Converters
{
internal class UInt53PropertyConverter : IPropertyConverter<ulong>
internal class UInt53PropertyConverter : IJsonPropertyConverter<ulong>
{
public ulong ReadJson(JsonReader reader, bool read = true)
public ulong Read(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.Number)
throw new SerializationException("Bad input, expected Number");
return reader.GetUInt64();
return reader.ParseUInt64();
}
public void WriteJson(JsonWriter writer, ulong value)
public void Write(JsonWriter writer, ulong value)
=> writer.WriteValue(value);
}
}

+ 0
- 86
src/Discord.Net.Rest/Serialization/ModelMap.cs View File

@@ -1,86 +0,0 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.Json;

namespace Discord.Serialization
{
internal static class ModelMap
{
private static readonly ConcurrentDictionary<Type, object> _cache = new ConcurrentDictionary<Type, object>();

internal static ModelMap<T> For<T>()
where T : class, new()
{
return _cache.GetOrAdd(typeof(T), _ =>
{
var type = typeof(T).GetTypeInfo();
var properties = new Dictionary<string, PropertyMap<T>>();

var propInfos = type.DeclaredProperties.ToArray();
for (int i = 0; i < propInfos.Length; i++)
{
var propInfo = propInfos[i];
if (!propInfo.CanRead || !propInfo.CanWrite)
continue;

var propMap = PropertyMap.Create<T>(propInfo);
properties.Add(propMap.Key, propMap);
}

return new ModelMap<T>(properties);
}) as ModelMap<T>;
}
}

internal class ModelMap<T>
where T : class, new()
{
private readonly PropertyMap<T>[] _propertyList;
private readonly Dictionary<string, PropertyMap<T>> _properties;

public ModelMap(Dictionary<string, PropertyMap<T>> properties)
{
_properties = properties;
_propertyList = _properties.Values.ToArray();
}

public T ReadJson(JsonReader reader)
{
var model = new T();

if (!reader.Read() || reader.TokenType != JsonTokenType.StartObject)
throw new InvalidOperationException("Bad input, expected StartObject");
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
return model;
if (reader.TokenType != JsonTokenType.PropertyName)
throw new InvalidOperationException("Bad input, expected PropertyName");

string key = reader.GetString();
if (_properties.TryGetValue(key, out var property))
property.ReadJson(model, reader);
else
reader.Skip(); //Unknown property, skip

if (!reader.Read())
throw new InvalidOperationException("Bad input, expected Value");
}
throw new InvalidOperationException("Bad input, expected EndObject");
}
public void WriteJson(T model, JsonWriter writer)
{
writer.WriteObjectStart();
for (int i = 0; i < _propertyList.Length; i++)
{
var property = _propertyList[i];
writer.WriteStartAttribute(property.Key);
property.WriteJson(model, writer);
}
writer.WriteObjectEnd();
}
}
}

+ 0
- 61
src/Discord.Net.Rest/Serialization/PropertyMap.cs View File

@@ -1,61 +0,0 @@
using Discord.Serialization.Converters;
using System;
using System.Reflection;
using System.Text.Json;

namespace Discord.Serialization
{
internal static class PropertyMap
{
public static PropertyMap<TModel> Create<TModel>(PropertyInfo propInfo)
{
var type = typeof(PropertyMap<,>).MakeGenericType(typeof(TModel), propInfo.PropertyType);
return Activator.CreateInstance(type, propInfo) as PropertyMap<TModel>;
}
}

internal abstract class PropertyMap<TModel>
{
public string Key { get; protected set; }

public abstract void WriteJson(TModel model, JsonWriter writer);
public abstract void ReadJson(TModel model, JsonReader reader);
}

internal class PropertyMap<TModel, TProp> : PropertyMap<TModel>
{
private readonly IPropertyConverter<TProp> _converter;
private readonly Func<TModel, TProp> _getFunc;
private readonly Action<TModel, TProp> _setFunc;
public PropertyMap(PropertyInfo propInfo)
{
var jsonProperty = propInfo.GetCustomAttribute<ModelPropertyAttribute>();
if (jsonProperty != null)
Key = jsonProperty.Key;
else
Key = propInfo.Name;
_getFunc = propInfo.GetMethod.CreateDelegate(typeof(Func<TModel, TProp>)) as Func<TModel, TProp>;
_setFunc = propInfo.SetMethod.CreateDelegate(typeof(Action<TModel, TProp>)) as Action<TModel, TProp>;

_converter = Converter.For<TProp>();
}

private TProp GetValue(TModel model)
=> _getFunc(model);
private void SetValue(TModel model, TProp prop)
=> _setFunc(model, prop);

public override void WriteJson(TModel model, JsonWriter writer)
{
var value = GetValue(model);
_converter.WriteJson(writer, value);
}
public override void ReadJson(TModel model, JsonReader reader)
{
var value = _converter.ReadJson(reader);
SetValue(model, value);
}
}
}

+ 0
- 35
src/Discord.Net.Rest/Serialization/Serializer.cs View File

@@ -1,35 +0,0 @@
using System;
using System.Text;
using System.Text.Formatting;
using System.Text.Json;
using System.Threading.Tasks;

namespace Discord.Serialization
{
internal class Serializer
{
public static ScopedSerializer Global { get; } = new ScopedSerializer();

public static T ReadJson<T>(ReadOnlyBuffer<byte> data) where T : class, new() => Global.ReadJson<T>(data);
public static void WriteJson<T>(ArrayFormatter data, T obj) where T : class, new() => Global.WriteJson(data, obj);

public static ScopedSerializer CreateScope() => new ScopedSerializer();
}

internal class ScopedSerializer
{
private readonly AsyncEvent<Func<Exception, Task>> _errorEvent = new AsyncEvent<Func<Exception, Task>>();
public event Func<Exception, Task> Error
{
add { _errorEvent.Add(value); }
remove { _errorEvent.Remove(value); }
}

public T ReadJson<T>(ReadOnlyBuffer<byte> data)
where T : class, new()
=> ModelMap.For<T>().ReadJson(new JsonReader(data.Span, SymbolTable.InvariantUtf8));
public void WriteJson<T>(ArrayFormatter data, T obj)
where T : class, new()
=> ModelMap.For<T>().WriteJson(obj, new JsonWriter(data));
}
}

+ 5
- 5
src/Discord.Net.Rpc/DiscordRpcApiClient.cs View File

@@ -41,11 +41,11 @@ namespace Discord.API
}
public Task SetResultAsync(ReadOnlyBuffer<byte> data)
{
return Promise.TrySetResultAsync(Serializer.ReadJson<T>(data));
return Promise.TrySetResultAsync(Serializer.Json.Read<T>(data));
}
public Task SetExceptionAsync(ReadOnlyBuffer<byte> data)
{
var error = Serializer.ReadJson<ErrorEvent>(data);
var error = Serializer.Json.Read<ErrorEvent>(data);
return Promise.TrySetExceptionAsync(new RpcException(error.Code, error.Message));
}
}
@@ -72,7 +72,7 @@ namespace Discord.API
public ConnectionState ConnectionState { get; private set; }

public DiscordRpcApiClient(string clientId, string userAgent, string origin, RestClientProvider restClientProvider, WebSocketProvider webSocketProvider,
ScopedSerializer serializer, RetryMode defaultRetryMode = RetryMode.AlwaysRetry)
Serializer serializer, RetryMode defaultRetryMode = RetryMode.AlwaysRetry)
: base(restClientProvider, userAgent, serializer, defaultRetryMode)
{
_connectionLock = new SemaphoreSlim(1, 1);
@@ -99,7 +99,7 @@ namespace Discord.API
_decompressionStream.SetLength(_decompressionStream.Position);

_decompressionStream.Position = 0;
var msg = _serializer.ReadJson<RpcFrame>(_decompressionStream.ToReadOnlyBuffer());
var msg = _serializer.Read<RpcFrame>(_decompressionStream.ToReadOnlyBuffer());
if (msg != null)
{
await _receivedRpcEvent.InvokeAsync(msg.Cmd, msg.Event, msg.Data).ConfigureAwait(false);
@@ -110,7 +110,7 @@ namespace Discord.API
}
else
{
var msg = _serializer.ReadJson<RpcFrame>(data);
var msg = _serializer.Read<RpcFrame>(data);
if (msg != null)
{
await _receivedRpcEvent.InvokeAsync(msg.Cmd, msg.Event, msg.Data).ConfigureAwait(false);


+ 17
- 17
src/Discord.Net.Rpc/DiscordRpcClient.cs View File

@@ -16,7 +16,7 @@ namespace Discord.Rpc
private readonly ConnectionManager _connection;
private readonly Logger _rpcLogger;
private readonly SemaphoreSlim _stateLock, _authorizeLock;
private readonly ScopedSerializer _serializer;
private readonly Serializer _serializer;

public ConnectionState ConnectionState { get; private set; }
public IReadOnlyCollection<string> Scopes { get; private set; }
@@ -37,10 +37,10 @@ namespace Discord.Rpc
_authorizeLock = new SemaphoreSlim(1, 1);
_rpcLogger = LogManager.CreateLogger("RPC");

_serializer = Serializer.CreateScope();
_serializer.Error += async ex =>
_serializer = new Serializer(SerializationFormat.Json);
_serializer.Error += ex =>
{
await _rpcLogger.WarningAsync("Serializer Error", ex);
_rpcLogger.WarningAsync("Serializer Error", ex).GetAwaiter().GetResult();
};

SetApiClient(new API.DiscordRpcApiClient(clientId, DiscordRestConfig.UserAgent, origin, config.RestClientProvider, config.WebSocketProvider, _serializer));
@@ -293,7 +293,7 @@ namespace Discord.Rpc
case "READY":
{
await _rpcLogger.DebugAsync("Received Dispatch (READY)").ConfigureAwait(false);
var data = _serializer.ReadJson<ReadyEvent>(payload.Value);
var data = _serializer.Read<ReadyEvent>(payload.Value);

var options = new RequestOptions
{
@@ -335,7 +335,7 @@ namespace Discord.Rpc
case "CHANNEL_CREATE":
{
await _rpcLogger.DebugAsync("Received Dispatch (CHANNEL_CREATE)").ConfigureAwait(false);
var data = _serializer.ReadJson<ChannelSummary>(payload.Value);
var data = _serializer.Read<ChannelSummary>(payload.Value);
var channel = RpcChannelSummary.Create(data);

await _channelCreatedEvent.InvokeAsync(channel).ConfigureAwait(false);
@@ -346,7 +346,7 @@ namespace Discord.Rpc
case "GUILD_CREATE":
{
await _rpcLogger.DebugAsync("Received Dispatch (GUILD_CREATE)").ConfigureAwait(false);
var data = _serializer.ReadJson<GuildSummary>(payload.Value);
var data = _serializer.Read<GuildSummary>(payload.Value);
var guild = RpcGuildSummary.Create(data);

await _guildCreatedEvent.InvokeAsync(guild).ConfigureAwait(false);
@@ -355,7 +355,7 @@ namespace Discord.Rpc
case "GUILD_STATUS":
{
await _rpcLogger.DebugAsync("Received Dispatch (GUILD_STATUS)").ConfigureAwait(false);
var data = _serializer.ReadJson<GuildStatusEvent>(payload.Value);
var data = _serializer.Read<GuildStatusEvent>(payload.Value);
var guildStatus = RpcGuildStatus.Create(data);

await _guildStatusUpdatedEvent.InvokeAsync(guildStatus).ConfigureAwait(false);
@@ -366,7 +366,7 @@ namespace Discord.Rpc
case "VOICE_STATE_CREATE":
{
await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_CREATE)").ConfigureAwait(false);
var data = _serializer.ReadJson<ExtendedVoiceState>(payload.Value);
var data = _serializer.Read<ExtendedVoiceState>(payload.Value);
var voiceState = RpcVoiceState.Create(this, data);

await _voiceStateCreatedEvent.InvokeAsync(voiceState).ConfigureAwait(false);
@@ -375,7 +375,7 @@ namespace Discord.Rpc
case "VOICE_STATE_UPDATE":
{
await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_UPDATE)").ConfigureAwait(false);
var data = _serializer.ReadJson<ExtendedVoiceState>(payload.Value);
var data = _serializer.Read<ExtendedVoiceState>(payload.Value);
var voiceState = RpcVoiceState.Create(this, data);

await _voiceStateUpdatedEvent.InvokeAsync(voiceState).ConfigureAwait(false);
@@ -384,7 +384,7 @@ namespace Discord.Rpc
case "VOICE_STATE_DELETE":
{
await _rpcLogger.DebugAsync("Received Dispatch (VOICE_STATE_DELETE)").ConfigureAwait(false);
var data = _serializer.ReadJson<ExtendedVoiceState>(payload.Value);
var data = _serializer.Read<ExtendedVoiceState>(payload.Value);
var voiceState = RpcVoiceState.Create(this, data);

await _voiceStateDeletedEvent.InvokeAsync(voiceState).ConfigureAwait(false);
@@ -394,7 +394,7 @@ namespace Discord.Rpc
case "SPEAKING_START":
{
await _rpcLogger.DebugAsync("Received Dispatch (SPEAKING_START)").ConfigureAwait(false);
var data = _serializer.ReadJson<SpeakingEvent>(payload.Value);
var data = _serializer.Read<SpeakingEvent>(payload.Value);

await _speakingStartedEvent.InvokeAsync(data.UserId).ConfigureAwait(false);
}
@@ -402,7 +402,7 @@ namespace Discord.Rpc
case "SPEAKING_STOP":
{
await _rpcLogger.DebugAsync("Received Dispatch (SPEAKING_STOP)").ConfigureAwait(false);
var data = _serializer.ReadJson<SpeakingEvent>(payload.Value);
var data = _serializer.Read<SpeakingEvent>(payload.Value);

await _speakingStoppedEvent.InvokeAsync(data.UserId).ConfigureAwait(false);
}
@@ -410,7 +410,7 @@ namespace Discord.Rpc
case "VOICE_SETTINGS_UPDATE":
{
await _rpcLogger.DebugAsync("Received Dispatch (VOICE_SETTINGS_UPDATE)").ConfigureAwait(false);
var data = _serializer.ReadJson<API.Rpc.VoiceSettings>(payload.Value);
var data = _serializer.Read<API.Rpc.VoiceSettings>(payload.Value);
var settings = VoiceSettings.Create(data);

await _voiceSettingsUpdated.InvokeAsync(settings).ConfigureAwait(false);
@@ -421,7 +421,7 @@ namespace Discord.Rpc
case "MESSAGE_CREATE":
{
await _rpcLogger.DebugAsync("Received Dispatch (MESSAGE_CREATE)").ConfigureAwait(false);
var data = _serializer.ReadJson<MessageEvent>(payload.Value);
var data = _serializer.Read<MessageEvent>(payload.Value);
var msg = RpcMessage.Create(this, data.ChannelId, data.Message);

await _messageReceivedEvent.InvokeAsync(msg).ConfigureAwait(false);
@@ -430,7 +430,7 @@ namespace Discord.Rpc
case "MESSAGE_UPDATE":
{
await _rpcLogger.DebugAsync("Received Dispatch (MESSAGE_UPDATE)").ConfigureAwait(false);
var data = _serializer.ReadJson<MessageEvent>(payload.Value);
var data = _serializer.Read<MessageEvent>(payload.Value);
var msg = RpcMessage.Create(this, data.ChannelId, data.Message);

await _messageUpdatedEvent.InvokeAsync(msg).ConfigureAwait(false);
@@ -439,7 +439,7 @@ namespace Discord.Rpc
case "MESSAGE_DELETE":
{
await _rpcLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE)").ConfigureAwait(false);
var data = _serializer.ReadJson<MessageEvent>(payload.Value);
var data = _serializer.Read<MessageEvent>(payload.Value);

await _messageDeletedEvent.InvokeAsync(data.ChannelId, data.Message.Id).ConfigureAwait(false);
}


+ 95
- 0
src/Discord.Net.Serialization/ConverterCollection.cs View File

@@ -0,0 +1,95 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;

namespace Discord.Serialization
{
public class ConverterCollection
{
private class ConverterTypeCollection
{
public Type DefaultConverterType;
public List<(Func<PropertyInfo, bool> Condition, Type ConverterType)> Conditionals = new List<(Func<PropertyInfo, bool>, Type)>();
}

private static readonly MethodInfo _getConverterMethod
= typeof(ConverterCollection).GetTypeInfo().GetDeclaredMethod(nameof(Get));

private readonly ConcurrentDictionary<Type, object> _maps = new ConcurrentDictionary<Type, object>();
private readonly ConcurrentDictionary<Type, ConverterTypeCollection> _types = new ConcurrentDictionary<Type, ConverterTypeCollection>();
private readonly ConcurrentDictionary<Type, ConverterTypeCollection> _genericTypes = new ConcurrentDictionary<Type, ConverterTypeCollection>();

internal ConverterCollection() { }

public void Add<TType, TConverter>()
{
var converters = _types.GetOrAdd(typeof(TType), _ => new ConverterTypeCollection());
converters.DefaultConverterType = typeof(TConverter);
}
public void Add<TType, TConverter>(Func<PropertyInfo, bool> condition)
{
var converters = _types.GetOrAdd(typeof(TType), _ => new ConverterTypeCollection());
converters.Conditionals.Add((condition, typeof(TConverter)));
}

public void AddGeneric(Type openType, Type openConverterType)
{
if (openType.IsConstructedGenericType) throw new InvalidOperationException($"{nameof(openType)} must be an open generic");
if (openConverterType.IsConstructedGenericType) throw new InvalidOperationException($"{nameof(openConverterType)} must be an open generic");
var converters = _genericTypes.GetOrAdd(openType, _ => new ConverterTypeCollection());
converters.DefaultConverterType = openConverterType;
}
public void AddGeneric(Type openType, Type openConverterType, Func<PropertyInfo, bool> condition)
{
if (openType.IsConstructedGenericType) throw new InvalidOperationException($"{nameof(openType)} must be an open generic");
if (openConverterType.IsConstructedGenericType) throw new InvalidOperationException($"{nameof(openConverterType)} must be an open generic");
var converters = _genericTypes.GetOrAdd(openType, _ => new ConverterTypeCollection());
converters.Conditionals.Add((condition, openConverterType));
}
public object Get<TType>(PropertyInfo propInfo)
{
var typeInfo = typeof(TType).GetTypeInfo();

//Generic converters
if (typeInfo.IsGenericType)
{
var converterType = FindConverterType(typeInfo.GetGenericTypeDefinition(), _genericTypes, propInfo);
if (converterType != null)
{
var innerType = typeInfo.GenericTypeArguments[0];
converterType = converterType.MakeGenericType(innerType);
object innerConverter = GetInnerConverter(innerType, propInfo);
return Activator.CreateInstance(converterType, innerConverter);
}
}

//Normal converters
{
var converterType = FindConverterType(typeof(TType), _types, propInfo);
if (converterType != null)
return Activator.CreateInstance(converterType);
}

throw new InvalidOperationException($"Unsupported model type: {typeof(TType).Name}");
}
private object GetInnerConverter(Type type, PropertyInfo propInfo)
=> _getConverterMethod.MakeGenericMethod(type).Invoke(this, new object[] { propInfo });

private Type FindConverterType(Type type, ConcurrentDictionary<Type, ConverterTypeCollection> collection, PropertyInfo propInfo)
{
if (collection.TryGetValue(type, out var converters))
{
for (int i = 0; i < converters.Conditionals.Count; i++)
{
if (converters.Conditionals[i].Condition(propInfo))
return converters.Conditionals[i].ConverterType;
}
if (converters.DefaultConverterType != null)
return converters.DefaultConverterType;
}
return null;
}
}
}

+ 18
- 0
src/Discord.Net.Serialization/Discord.Net.Serialization.csproj View File

@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../../Discord.Net.targets" />
<PropertyGroup>
<AssemblyName>Discord.Net.Serialization</AssemblyName>
<RootNamespace>Discord.Serialization</RootNamespace>
<Description>The core components for the Discord.Net library.</Description>
<TargetFrameworks>net45;netstandard1.1;netstandard1.3</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Buffers" Version="4.4.0-preview2-25405-01" />
<PackageReference Include="System.Collections.Immutable" Version="1.3.1" />
<PackageReference Include="System.Interactive.Async" Version="3.1.1" />
<PackageReference Include="System.Memory" Version="4.4.0-preview2-25405-01" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.4.0-preview2-25405-01" />
<PackageReference Include="System.ValueTuple" Version="4.3.1" />
</ItemGroup>
</Project>

+ 111
- 0
src/Discord.Net.Serialization/Extensions/BufferExtensions.cs View File

@@ -0,0 +1,111 @@
using System;
using System.Text;
using System.Text.Utf8;

namespace Discord.Serialization
{
public static class BufferExtensions
{
private static readonly ParsedFormat _numberFormat = new ParsedFormat('D');

public static bool ParseBool(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseBoolean(text, out bool result, out int ignored, SymbolTable.InvariantUtf8))
return result;
throw new SerializationException("Failed to parse Boolean");
}

public static sbyte ParseInt8(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseSByte(text, out sbyte result, out int ignored, _numberFormat, SymbolTable.InvariantUtf8))
return result;
throw new SerializationException("Failed to parse Int8");
}
public static short ParseInt16(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseInt16(text, out short result, out int ignored, _numberFormat, SymbolTable.InvariantUtf8))
return result;
throw new SerializationException("Failed to parse Int16");
}
public static int ParseInt32(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseInt32(text, out int result, out int ignored, _numberFormat, SymbolTable.InvariantUtf8))
return result;
throw new SerializationException("Failed to parse Int32");
}
public static long ParseInt64(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseInt64(text, out long result, out int ignored, _numberFormat, SymbolTable.InvariantUtf8))
return result;
throw new SerializationException("Failed to parse Int64");
}

public static byte ParseUInt8(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseByte(text, out byte result, out int ignored, _numberFormat, SymbolTable.InvariantUtf8))
return result;
throw new SerializationException("Failed to parse UInt8");
}
public static ushort ParseUInt16(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseUInt16(text, out ushort result, out int ignored, _numberFormat, SymbolTable.InvariantUtf8))
return result;
throw new SerializationException("Failed to parse UInt16");
}
public static uint ParseUInt32(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseUInt32(text, out uint result, out int ignored, _numberFormat, SymbolTable.InvariantUtf8))
return result;
throw new SerializationException("Failed to parse UInt32");
}
public static ulong ParseUInt64(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseUInt64(text, out ulong result, out int ignored, _numberFormat, SymbolTable.InvariantUtf8))
return result;
throw new SerializationException("Failed to parse UInt64");
}

public static char ParseChar(this ReadOnlySpan<byte> text)
{
string str = ParseString(text);
if (str.Length == 1)
return str[0];
throw new SerializationException("Failed to parse Char");
}
public static string ParseString(this ReadOnlySpan<byte> text) => new Utf8String(text).ToString();

public static float ParseSingle(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseDecimal(text, out decimal result, out int ignored, SymbolTable.InvariantUtf8))
return (float)result;
throw new SerializationException("Failed to parse Single");
}
public static double ParseDouble(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseDecimal(text, out decimal result, out int ignored, SymbolTable.InvariantUtf8))
return (double)result;
throw new SerializationException("Failed to parse Double");
}
public static decimal ParseDecimal(this ReadOnlySpan<byte> text)
{
if (PrimitiveParser.TryParseDecimal(text, out decimal result, out int ignored, SymbolTable.InvariantUtf8))
return result;
throw new SerializationException("Failed to parse Decimal");
}

public static DateTime ParseDateTime(this ReadOnlySpan<byte> text)
{
string str = ParseString(text);
if (DateTime.TryParse(str, out var result)) //TODO: Improve perf
return result;
throw new SerializationException("Failed to parse DateTime");
}
public static DateTimeOffset ParseDateTimeOffset(this ReadOnlySpan<byte> text)
{
string str = ParseString(text);
if (DateTimeOffset.TryParse(str, out var result)) //TODO: Improve perf
return result;
throw new SerializationException("Failed to parse DateTimeOffset");
}
}
}

+ 36
- 0
src/Discord.Net.Serialization/Extensions/JsonReaderExtensions.cs View File

@@ -0,0 +1,36 @@
using System;
using System.Text.Json;

namespace Discord.Serialization
{
public static class JsonReaderExtensions
{
public static bool ParseBool(this JsonReader reader) => reader.Value.ParseBool();

public static sbyte ParseInt8(this JsonReader reader) => reader.Value.ParseInt8();
public static short ParseInt16(this JsonReader reader) => reader.Value.ParseInt16();
public static int ParseInt32(this JsonReader reader) => reader.Value.ParseInt32();
public static long ParseInt64(this JsonReader reader) => reader.Value.ParseInt64();

public static byte ParseUInt8(this JsonReader reader) => reader.Value.ParseUInt8();
public static ushort ParseUInt16(this JsonReader reader) => reader.Value.ParseUInt16();
public static uint ParseUInt32(this JsonReader reader) => reader.Value.ParseUInt32();
public static ulong ParseUInt64(this JsonReader reader) => reader.Value.ParseUInt64();

public static char ParseChar(this JsonReader reader) => reader.Value.ParseChar();
public static string ParseString(this JsonReader reader) => reader.Value.ParseString();

public static float ParseSingle(this JsonReader reader) => reader.Value.ParseSingle();
public static double ParseDouble(this JsonReader reader) => reader.Value.ParseDouble();
public static decimal ParseDecimal(this JsonReader reader) => reader.Value.ParseDecimal();

public static DateTime ParseDateTime(this JsonReader reader) => reader.Value.ParseDateTime();
public static DateTimeOffset ParseDateTimeOffset(this JsonReader reader) => reader.Value.ParseDateTimeOffset();

public static void Skip(this JsonReader reader)
{
int initialDepth = reader._depth;
while (reader.Read() && reader._depth > initialDepth) { }
}
}
}

src/Discord.Net.Rest/Serialization/Converters/ListPropertyConverter.cs → src/Discord.Net.Serialization/Json/Converters/Collections.cs View File

@@ -1,33 +1,33 @@
using System.Collections.Generic;
using System.Text.Json;

namespace Discord.Serialization.Converters
namespace Discord.Serialization.Json.Converters
{
internal class ListPropertyConverter<T> : IPropertyConverter<List<T>>
internal class ListPropertyConverter<T> : IJsonPropertyConverter<List<T>>
{
private readonly IPropertyConverter<T> _innerConverter;
private readonly IJsonPropertyConverter<T> _innerConverter;

public ListPropertyConverter(IPropertyConverter<T> innerConverter)
public ListPropertyConverter(IJsonPropertyConverter<T> innerConverter)
{
_innerConverter = innerConverter;
}

public List<T> ReadJson(JsonReader reader, bool read = true)
public List<T> Read(JsonReader reader, bool read = true)
{
if ((read && !reader.Read()) || reader.TokenType != JsonTokenType.StartArray)
throw new SerializationException("Bad input, expected StartArray");

var list = new List<T>();
while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
list.Add(_innerConverter.ReadJson(reader));
list.Add(_innerConverter.Read(reader));
return list;
}

public void WriteJson(JsonWriter writer, List<T> value)
public void Write(JsonWriter writer, List<T> value)
{
writer.WriteArrayStart();
for (int i = 0; i < value.Count; i++)
_innerConverter.WriteJson(writer, value[i]);
_innerConverter.Write(writer, value[i]);
writer.WriteArrayEnd();
}
}

+ 32
- 0
src/Discord.Net.Serialization/Json/Converters/Nullable.cs View File

@@ -0,0 +1,32 @@
using System.Text.Json;

namespace Discord.Serialization.Json.Converters
{
internal class NullablePropertyConverter<T> : IJsonPropertyConverter<T?>
where T : struct
{
private readonly IJsonPropertyConverter<T> _innerConverter;

public NullablePropertyConverter(IJsonPropertyConverter<T> innerConverter)
{
_innerConverter = innerConverter;
}

public T? Read(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType == JsonValueType.Null)
return null;
return _innerConverter.Read(reader, false);
}

public void Write(JsonWriter writer, T? value)
{
if (value.HasValue)
_innerConverter.Write(writer, value.Value);
else
writer.WriteNull();
}
}
}

src/Discord.Net.Rest/Serialization/Converters/Primitives/BooleanPropertyConverter.cs → src/Discord.Net.Serialization/Json/Converters/Primitives.Bool.cs View File

@@ -1,10 +1,10 @@
using System.Text.Json;

namespace Discord.Serialization.Converters
namespace Discord.Serialization.Json.Converters
{
internal class BooleanPropertyConverter : IPropertyConverter<bool>
internal class BooleanPropertyConverter : IJsonPropertyConverter<bool>
{
public bool ReadJson(JsonReader reader, bool read = true)
public bool Read(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
@@ -15,7 +15,7 @@ namespace Discord.Serialization.Converters
default: throw new SerializationException("Bad input, expected False or True");
}
}
public void WriteJson(JsonWriter writer, bool value)
public void Write(JsonWriter writer, bool value)
=> writer.WriteValue(value);
}
}

+ 33
- 0
src/Discord.Net.Serialization/Json/Converters/Primitives.DateTime.cs View File

@@ -0,0 +1,33 @@
using System;
using System.Text.Json;

namespace Discord.Serialization.Json.Converters
{
internal class DateTimePropertyConverter : IJsonPropertyConverter<DateTime>
{
public DateTime Read(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.String)
throw new SerializationException("Bad input, expected String");
return reader.ParseDateTime();
}
public void Write(JsonWriter writer, DateTime value)
=> writer.WriteValue(value);
}

internal class DateTimeOffsetPropertyConverter : IJsonPropertyConverter<DateTimeOffset>
{
public DateTimeOffset Read(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.String)
throw new SerializationException("Bad input, expected String");
return reader.ParseDateTimeOffset();
}
public void Write(JsonWriter writer, DateTimeOffset value)
=> writer.WriteValue(value);
}
}

+ 12
- 0
src/Discord.Net.Serialization/Json/Converters/Primitives.Enum.cs View File

@@ -0,0 +1,12 @@
using System.Text.Json;

namespace Discord.Serialization.Json.Converters
{
internal class EnumPropertyConverter<T> : IJsonPropertyConverter<T>
{
public T Read(JsonReader reader, bool read = true)
=> throw new System.NotImplementedException();
public void Write(JsonWriter writer, T value)
=> throw new System.NotImplementedException();
}
}

+ 46
- 0
src/Discord.Net.Serialization/Json/Converters/Primitives.Float.cs View File

@@ -0,0 +1,46 @@
using System.Text.Json;

namespace Discord.Serialization.Json.Converters
{
internal class SinglePropertyConverter : IJsonPropertyConverter<float>
{
public float Read(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.Number)
throw new SerializationException("Bad input, expected Number");
return reader.ParseSingle();
}
public void Write(JsonWriter writer, float value)
=> writer.WriteValue(value.ToString());
}

internal class DoublePropertyConverter : IJsonPropertyConverter<double>
{
public double Read(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.Number)
throw new SerializationException("Bad input, expected Number");
return reader.ParseDouble();
}
public void Write(JsonWriter writer, double value)
=> writer.WriteValue(value.ToString());
}

internal class DecimalPropertyConverter : IJsonPropertyConverter<decimal>
{
public decimal Read(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.Number)
throw new SerializationException("Bad input, expected Number");
return reader.ParseDecimal();
}
public void Write(JsonWriter writer, decimal value)
=> writer.WriteValue(value.ToString());
}
}

+ 60
- 0
src/Discord.Net.Serialization/Json/Converters/Primitives.Signed.cs View File

@@ -0,0 +1,60 @@
using System.Text.Json;

namespace Discord.Serialization.Json.Converters
{
internal class Int8PropertyConverter : IJsonPropertyConverter<sbyte>
{
public sbyte Read(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.Number)
throw new SerializationException("Bad input, expected Number");
return reader.ParseInt8();
}
public void Write(JsonWriter writer, sbyte value)
=> writer.WriteValue(value);
}

internal class Int16PropertyConverter : IJsonPropertyConverter<short>
{
public short Read(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.Number)
throw new SerializationException("Bad input, expected Number");
return reader.ParseInt16();
}
public void Write(JsonWriter writer, short value)
=> writer.WriteValue(value);
}

internal class Int32PropertyConverter : IJsonPropertyConverter<int>
{
public int Read(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.Number)
throw new SerializationException("Bad input, expected Number");
return reader.ParseInt32();
}
public void Write(JsonWriter writer, int value)
=> writer.WriteValue(value);
}

internal class Int64PropertyConverter : IJsonPropertyConverter<long>
{
public long Read(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.String)
throw new SerializationException("Bad input, expected String");
return reader.ParseInt64();
}
public void Write(JsonWriter writer, long value)
=> writer.WriteValue(value.ToString());
}
}

+ 32
- 0
src/Discord.Net.Serialization/Json/Converters/Primitives.String.cs View File

@@ -0,0 +1,32 @@
using System.Text.Json;

namespace Discord.Serialization.Json.Converters
{
internal class CharPropertyConverter : IJsonPropertyConverter<char>
{
public char Read(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.String)
throw new SerializationException("Bad input, expected String");
return reader.ParseChar();
}
public void Write(JsonWriter writer, char value)
=> writer.WriteValue(value);
}

internal class StringPropertyConverter : IJsonPropertyConverter<string>
{
public string Read(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.String)
throw new SerializationException("Bad input, expected String");
return reader.ParseString();
}
public void Write(JsonWriter writer, string value)
=> writer.WriteValue(value);
}
}

+ 60
- 0
src/Discord.Net.Serialization/Json/Converters/Primitives.Unsigned.cs View File

@@ -0,0 +1,60 @@
using System.Text.Json;

namespace Discord.Serialization.Json.Converters
{
internal class UInt8PropertyConverter : IJsonPropertyConverter<byte>
{
public byte Read(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.Number)
throw new SerializationException("Bad input, expected Number");
return reader.ParseUInt8();
}
public void Write(JsonWriter writer, byte value)
=> writer.WriteValue(value);
}

internal class UInt16PropertyConverter : IJsonPropertyConverter<ushort>
{
public ushort Read(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.Number)
throw new SerializationException("Bad input, expected Number");
return reader.ParseUInt16();
}
public void Write(JsonWriter writer, ushort value)
=> writer.WriteValue(value);
}

internal class UInt32PropertyConverter : IJsonPropertyConverter<uint>
{
public uint Read(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.Number)
throw new SerializationException("Bad input, expected Number");
return reader.ParseUInt32();
}
public void Write(JsonWriter writer, uint value)
=> writer.WriteValue(value);
}

internal class UInt64PropertyConverter : IJsonPropertyConverter<ulong>
{
public ulong Read(JsonReader reader, bool read = true)
{
if (read)
reader.Read();
if (reader.ValueType != JsonValueType.String)
throw new SerializationException("Bad input, expected String");
return reader.ParseUInt64();
}
public void Write(JsonWriter writer, ulong value)
=> writer.WriteValue(value.ToString());
}
}

+ 10
- 0
src/Discord.Net.Serialization/Json/IJsonPropertyConverter.cs View File

@@ -0,0 +1,10 @@
using System.Text.Json;

namespace Discord.Serialization.Json
{
public interface IJsonPropertyConverter<T>
{
T Read(JsonReader reader, bool read = true);
void Write(JsonWriter writer, T value);
}
}

+ 12
- 0
src/Discord.Net.Serialization/Json/IJsonPropertyMap.cs View File

@@ -0,0 +1,12 @@
using System.Text.Json;

namespace Discord.Serialization
{
internal interface IJsonPropertyMap<TModel>
{
string Key { get; }

void Write(TModel model, JsonWriter writer);
void Read(TModel model, JsonReader reader);
}
}

+ 102
- 0
src/Discord.Net.Serialization/Json/JsonFormat.cs View File

@@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Text.Formatting;
using System.Text.Json;

namespace Discord.Serialization.Json
{
public class JsonFormat : SerializationFormat
{
public JsonFormat()
{
AddConverter<bool, Converters.BooleanPropertyConverter>();

AddConverter<sbyte, Converters.Int8PropertyConverter>();
AddConverter<short, Converters.Int16PropertyConverter>();
AddConverter<int, Converters.Int32PropertyConverter>();
AddConverter<long, Converters.Int64PropertyConverter>();

AddConverter<byte, Converters.UInt8PropertyConverter>();
AddConverter<ushort, Converters.UInt16PropertyConverter>();
AddConverter<uint, Converters.UInt32PropertyConverter>();
AddConverter<ulong, Converters.UInt64PropertyConverter>();

AddConverter<float, Converters.SinglePropertyConverter>();
AddConverter<double, Converters.DoublePropertyConverter>();
AddConverter<decimal, Converters.DecimalPropertyConverter>();

AddConverter<char, Converters.CharPropertyConverter>();
AddConverter<string, Converters.StringPropertyConverter>();

AddConverter<DateTime, Converters.DateTimePropertyConverter>();
AddConverter<DateTimeOffset, Converters.DateTimeOffsetPropertyConverter>();

AddGenericConverter(typeof(List<>), typeof(Converters.ListPropertyConverter<>));
AddGenericConverter(typeof(Nullable<>), typeof(Converters.NullablePropertyConverter<>));

//AddEnumConverter<Converters.EnumPropertyConverter>();
}

public void AddConverter<TValue, TConverter>()
where TConverter : class, IJsonPropertyConverter<TValue>
=> _converters.Add<TValue, TConverter>();
public void AddConverter<TValue, TConverter>(Func<PropertyInfo, bool> condition)
where TConverter : class, IJsonPropertyConverter<TValue>
=> _converters.Add<TValue, TConverter>(condition);

public void AddGenericConverter(Type value, Type converter)
=> _converters.AddGeneric(value, converter);
public void AddGenericConverter(Type value, Type converter, Func<PropertyInfo, bool> condition)
=> _converters.AddGeneric(value, converter);

protected override PropertyMap CreatePropertyMap<TModel, TValue>(PropertyInfo propInfo)
{
var converter = (IJsonPropertyConverter<TValue>)_converters.Get<TValue>(propInfo);
return new JsonPropertyMap<TModel, TValue>(propInfo, converter);
}

protected internal override TModel Read<TModel>(Serializer serializer, ReadOnlyBuffer<byte> data)
{
var reader = new JsonReader(data.Span, SymbolTable.InvariantUtf8);
var map = MapModel<TModel>();
var model = new TModel();

if (!reader.Read() || reader.TokenType != JsonTokenType.StartObject)
throw new InvalidOperationException("Bad input, expected StartObject");
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
return model;
if (reader.TokenType != JsonTokenType.PropertyName)
throw new InvalidOperationException("Bad input, expected PropertyName");

string key = reader.ParseString();
if (map.PropertiesByKey.TryGetValue(key, out var property))
(property as IJsonPropertyMap<TModel>).Read(model, reader);
else
reader.Skip(); //Unknown property, skip

if (!reader.Read())
throw new InvalidOperationException("Bad input, expected Value");
}
throw new InvalidOperationException("Bad input, expected EndObject");
}

protected internal override void Write<TModel>(Serializer serializer, ArrayFormatter stream, TModel model)
{
var writer = new JsonWriter(stream);
var map = MapModel<TModel>();

writer.WriteObjectStart();
for (int i = 0; i < map.Properties.Length; i++)
{
var property = map.Properties[i];
writer.WriteStartAttribute(property.Key);
(property as IJsonPropertyMap<TModel>).Write(model, writer);
}
writer.WriteObjectEnd();
}
}
}

+ 33
- 0
src/Discord.Net.Serialization/Json/JsonPropertyMap.cs View File

@@ -0,0 +1,33 @@
using System;
using System.Reflection;
using System.Text.Json;

namespace Discord.Serialization.Json
{
internal class JsonPropertyMap<TModel, TType> : PropertyMap, IJsonPropertyMap<TModel>
{
private readonly IJsonPropertyConverter<TType> _converter;
private readonly Func<TModel, TType> _getFunc;
private readonly Action<TModel, TType> _setFunc;

public JsonPropertyMap(PropertyInfo propInfo, IJsonPropertyConverter<TType> converter)
: base(propInfo)
{
_converter = converter;

_getFunc = propInfo.GetMethod.CreateDelegate(typeof(Func<TModel, TType>)) as Func<TModel, TType>;
_setFunc = propInfo.SetMethod.CreateDelegate(typeof(Action<TModel, TType>)) as Action<TModel, TType>;
}

public void Write(TModel model, JsonWriter writer)
{
var value = _getFunc(model);
_converter.Write(writer, value);
}
public void Read(TModel model, JsonReader reader)
{
var value = _converter.Read(reader);
_setFunc(model, value);
}
}
}

+ 18
- 0
src/Discord.Net.Serialization/ModelMap.cs View File

@@ -0,0 +1,18 @@
using System.Collections.Generic;
using System.Linq;

namespace Discord.Serialization
{
public class ModelMap<TModel>
where TModel : class, new()
{
public readonly PropertyMap[] Properties;
public readonly Dictionary<string, PropertyMap> PropertiesByKey;

public ModelMap(Dictionary<string, PropertyMap> properties)
{
PropertiesByKey = properties;
Properties = PropertiesByKey.Values.ToArray();
}
}
}

src/Discord.Net.Rest/Serialization/ModelPropertyAttribute.cs → src/Discord.Net.Serialization/ModelPropertyAttribute.cs View File

@@ -2,7 +2,7 @@

namespace Discord.Serialization
{
internal class ModelPropertyAttribute : Attribute
public class ModelPropertyAttribute : Attribute
{
public string Key { get; }
public bool IgnoreNull { get; set; }

+ 18
- 0
src/Discord.Net.Serialization/PropertyMap.cs View File

@@ -0,0 +1,18 @@
using System.Reflection;

namespace Discord.Serialization
{
public abstract class PropertyMap
{
public string Key { get; }

public PropertyMap(PropertyInfo propInfo)
{
var jsonProperty = propInfo.GetCustomAttribute<ModelPropertyAttribute>();
if (jsonProperty != null)
Key = jsonProperty.Key;
else
Key = propInfo.Name;
}
}
}

src/Discord.Net.Rest/Serialization/SerializationException.cs → src/Discord.Net.Serialization/SerializationException.cs View File

@@ -2,7 +2,7 @@

namespace Discord.Serialization
{
internal class SerializationException : Exception
public class SerializationException : Exception
{
public SerializationException()
: base("Serialization failed")

+ 57
- 0
src/Discord.Net.Serialization/SerializationFormat.cs View File

@@ -0,0 +1,57 @@
using Discord.Serialization.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.Formatting;

namespace Discord.Serialization
{
public abstract class SerializationFormat
{
private static readonly MethodInfo _getConverterMethod
= typeof(SerializationFormat).GetTypeInfo().GetDeclaredMethod(nameof(CreatePropertyMap));

private static readonly Lazy<JsonFormat> _json = new Lazy<JsonFormat>(() => new JsonFormat());
public static JsonFormat Json => _json.Value;

protected readonly ConcurrentDictionary<Type, object> _maps = new ConcurrentDictionary<Type, object>();
protected readonly ConverterCollection _converters = new ConverterCollection();

protected ModelMap<TModel> MapModel<TModel>()
where TModel : class, new()
{
return _maps.GetOrAdd(typeof(TModel), _ =>
{
var type = typeof(TModel).GetTypeInfo();
var properties = new Dictionary<string, PropertyMap>();

var propInfos = type.DeclaredProperties.ToArray();
for (int i = 0; i < propInfos.Length; i++)
{
var propInfo = propInfos[i];
if (!propInfo.CanRead || !propInfo.CanWrite)
continue;

var propMap = MapProperty<TModel>(propInfo);
properties.Add(propMap.Key, propMap);
}
return new ModelMap<TModel>(properties);
}) as ModelMap<TModel>;
}

private PropertyMap MapProperty<TModel>(PropertyInfo propInfo)
where TModel : class, new()
=> _getConverterMethod.MakeGenericMethod(typeof(TModel), propInfo.PropertyType).Invoke(this, new object[] { propInfo }) as PropertyMap;


protected internal abstract TModel Read<TModel>(Serializer serializer, ReadOnlyBuffer<byte> data)
where TModel : class, new();
protected internal abstract void Write<TModel>(Serializer serializer, ArrayFormatter stream, TModel model)
where TModel : class, new();

protected abstract PropertyMap CreatePropertyMap<TModel, TValue>(PropertyInfo propInfo)
where TModel : class, new();
}
}

+ 27
- 0
src/Discord.Net.Serialization/Serializer.cs View File

@@ -0,0 +1,27 @@
using System;
using System.Text.Formatting;

namespace Discord.Serialization
{
public class Serializer
{
private readonly static Lazy<Serializer> _json = new Lazy<Serializer>(() => new Serializer(SerializationFormat.Json));
public static Serializer Json => _json.Value;

public event Action<Exception> Error; //TODO: Impl

private readonly SerializationFormat _format;

public Serializer(SerializationFormat format)
{
_format = format;
}

public T Read<T>(ReadOnlyBuffer<byte> data)
where T : class, new()
=> _format.Read<T>(this, data);
public void Write<T>(ArrayFormatter data, T obj)
where T : class, new()
=> _format.Write(this, data, obj);
}
}

src/Discord.Net.Core/_corefxlab/BufferExtensions.cs → src/Discord.Net.Serialization/_corefxlab/ExperimentalBufferExtensions.cs View File


src/Discord.Net.Core/_corefxlab/System.Binary/System/Binary/BufferReader.cs → src/Discord.Net.Serialization/_corefxlab/System.Binary/System/Binary/BufferReader.cs View File


src/Discord.Net.Core/_corefxlab/System.Binary/System/Binary/BufferWriter.cs → src/Discord.Net.Serialization/_corefxlab/System.Binary/System/Binary/BufferWriter.cs View File


src/Discord.Net.Core/_corefxlab/System.Binary/System/Binary/UnsafeUtilities.cs → src/Discord.Net.Serialization/_corefxlab/System.Binary/System/Binary/UnsafeUtilities.cs View File


src/Discord.Net.Core/_corefxlab/System.Buffers.Primitives/System/Buffers/Buffer.cs → src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Buffers/Buffer.cs View File


src/Discord.Net.Core/_corefxlab/System.Buffers.Primitives/System/Buffers/BufferExtensions.cs → src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Buffers/BufferExtensions.cs View File


src/Discord.Net.Core/_corefxlab/System.Buffers.Primitives/System/Buffers/BufferHandle.cs → src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Buffers/BufferHandle.cs View File


src/Discord.Net.Core/_corefxlab/System.Buffers.Primitives/System/Buffers/BufferPool.cs → src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Buffers/BufferPool.cs View File


src/Discord.Net.Core/_corefxlab/System.Buffers.Primitives/System/Buffers/IOutput.cs → src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Buffers/IOutput.cs View File


src/Discord.Net.Core/_corefxlab/System.Buffers.Primitives/System/Buffers/IRetainable.cs → src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Buffers/IRetainable.cs View File


src/Discord.Net.Core/_corefxlab/System.Buffers.Primitives/System/Buffers/OwnedBuffer.cs → src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Buffers/OwnedBuffer.cs View File


src/Discord.Net.Core/_corefxlab/System.Buffers.Primitives/System/Buffers/ReadOnlyBuffer.cs → src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Buffers/ReadOnlyBuffer.cs View File


src/Discord.Net.Core/_corefxlab/System.Buffers.Primitives/System/Buffers/Transformation.cs → src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Buffers/Transformation.cs View File


src/Discord.Net.Core/_corefxlab/System.Buffers.Primitives/System/Internal/ManagedBufferPool.cs → src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Internal/ManagedBufferPool.cs View File


src/Discord.Net.Core/_corefxlab/System.Buffers.Primitives/System/Internal/OwnedArray.cs → src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Internal/OwnedArray.cs View File


src/Discord.Net.Core/_corefxlab/System.Buffers.Primitives/System/Runtime/BufferDebuggerView.cs → src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Runtime/BufferDebuggerView.cs View File


src/Discord.Net.Core/_corefxlab/System.Buffers.Primitives/System/Runtime/BufferPrimitivesThrowHelper.cs → src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Runtime/BufferPrimitivesThrowHelper.cs View File


src/Discord.Net.Core/_corefxlab/System.Buffers.Primitives/System/Runtime/Contract.cs → src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Runtime/Contract.cs View File


src/Discord.Net.Core/_corefxlab/System.Buffers.Primitives/System/Runtime/HashingHelper.cs → src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Runtime/HashingHelper.cs View File


src/Discord.Net.Core/_corefxlab/System.Buffers.Primitives/System/Runtime/PrimitiveAttribute.cs → src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Runtime/PrimitiveAttribute.cs View File


src/Discord.Net.Core/_corefxlab/System.Buffers.Primitives/System/Runtime/ReadOnlyBufferDebuggerView.cs → src/Discord.Net.Serialization/_corefxlab/System.Buffers.Primitives/System/Runtime/ReadOnlyBufferDebuggerView.cs View File


src/Discord.Net.Core/_corefxlab/System.Collections.Sequences/System/Collections/Sequences/ArrayList.cs → src/Discord.Net.Serialization/_corefxlab/System.Collections.Sequences/System/Collections/Sequences/ArrayList.cs View File


src/Discord.Net.Core/_corefxlab/System.Collections.Sequences/System/Collections/Sequences/ISequence.cs → src/Discord.Net.Serialization/_corefxlab/System.Collections.Sequences/System/Collections/Sequences/ISequence.cs View File


src/Discord.Net.Core/_corefxlab/System.Collections.Sequences/System/Collections/Sequences/Position.cs → src/Discord.Net.Serialization/_corefxlab/System.Collections.Sequences/System/Collections/Sequences/Position.cs View File


src/Discord.Net.Core/_corefxlab/System.Collections.Sequences/System/Collections/Sequences/ResizableArray.cs → src/Discord.Net.Serialization/_corefxlab/System.Collections.Sequences/System/Collections/Sequences/ResizableArray.cs View File


src/Discord.Net.Core/_corefxlab/System.Collections.Sequences/System/Collections/Sequences/SequenceEnumerator.cs → src/Discord.Net.Serialization/_corefxlab/System.Collections.Sequences/System/Collections/Sequences/SequenceEnumerator.cs View File


src/Discord.Net.Core/_corefxlab/System.Text.Formatting/System/Text/Formatting/CompositeFormat.cs → src/Discord.Net.Serialization/_corefxlab/System.Text.Formatting/System/Text/Formatting/CompositeFormat.cs View File


src/Discord.Net.Core/_corefxlab/System.Text.Formatting/System/Text/Formatting/Formatters/ArrayFormatter.cs → src/Discord.Net.Serialization/_corefxlab/System.Text.Formatting/System/Text/Formatting/Formatters/ArrayFormatter.cs View File


src/Discord.Net.Core/_corefxlab/System.Text.Formatting/System/Text/Formatting/Formatters/OutputFormatter.cs → src/Discord.Net.Serialization/_corefxlab/System.Text.Formatting/System/Text/Formatting/Formatters/OutputFormatter.cs View File


src/Discord.Net.Core/_corefxlab/System.Text.Formatting/System/Text/Formatting/Formatters/SequenceFormatter.cs → src/Discord.Net.Serialization/_corefxlab/System.Text.Formatting/System/Text/Formatting/Formatters/SequenceFormatter.cs View File


src/Discord.Net.Core/_corefxlab/System.Text.Formatting/System/Text/Formatting/Formatters/StreamFormatter.cs → src/Discord.Net.Serialization/_corefxlab/System.Text.Formatting/System/Text/Formatting/Formatters/StreamFormatter.cs View File


src/Discord.Net.Core/_corefxlab/System.Text.Formatting/System/Text/Formatting/Formatters/StringFormatter.cs → src/Discord.Net.Serialization/_corefxlab/System.Text.Formatting/System/Text/Formatting/Formatters/StringFormatter.cs View File


src/Discord.Net.Core/_corefxlab/System.Text.Formatting/System/Text/Formatting/IOutputExtensions.cs → src/Discord.Net.Serialization/_corefxlab/System.Text.Formatting/System/Text/Formatting/IOutputExtensions.cs View File


src/Discord.Net.Core/_corefxlab/System.Text.Formatting/System/Text/Formatting/ITextOutput.cs → src/Discord.Net.Serialization/_corefxlab/System.Text.Formatting/System/Text/Formatting/ITextOutput.cs View File


src/Discord.Net.Core/_corefxlab/System.Text.Formatting/System/Text/Formatting/ITextOutputExtensions.cs → src/Discord.Net.Serialization/_corefxlab/System.Text.Formatting/System/Text/Formatting/ITextOutputExtensions.cs View File


src/Discord.Net.Core/_corefxlab/System.Text.Formatting/System/Text/Parsing/SequenceParser.cs → src/Discord.Net.Serialization/_corefxlab/System.Text.Formatting/System/Text/Parsing/SequenceParser.cs View File


src/Discord.Net.Rest/_corefxlab/System.Text.Json/System/Text/Json/JsonConstants.cs → src/Discord.Net.Serialization/_corefxlab/System.Text.Json/System/Text/Json/JsonConstants.cs View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save