From c50bad90c24cebc0cbc87259a808eb9518f9c53c Mon Sep 17 00:00:00 2001 From: Gerard Gunnewijk Date: Tue, 20 Aug 2024 23:35:03 +0200 Subject: [PATCH] Update to only use .NET 8 and add support for System.Text.Json serialization --- .github/workflows/dotnet-core.yml | 4 +- Directory.Build.props | 55 +-- src/Directory.Build.props | 2 +- .../JsonConverters/PointJsonConverter.cs | 57 +++ .../JsonConverters/RectangleJsonConverter.cs | 65 +++ .../JsonConverters/SizeJsonConverter.cs | 57 +++ .../JsonConverters/SpacingJsonConverter.cs | 65 +++ .../JsonConverters/UnitJsonConverter.cs | 29 ++ .../JsonConverters/ValueJsonConverter.cs | 57 +++ .../PackageDetails.props | 2 +- src/Synercoding.Primitives/Point.cs | 30 +- src/Synercoding.Primitives/Rectangle.cs | 34 +- src/Synercoding.Primitives/Size.cs | 30 +- src/Synercoding.Primitives/Spacing.cs | 46 +- .../Synercoding.Primitives.csproj | 12 - src/Synercoding.Primitives/System/HashCode.cs | 446 ------------------ src/Synercoding.Primitives/Unit.cs | 32 +- src/Synercoding.Primitives/Value.cs | 30 +- tests/Directory.Build.props | 2 +- tests/Directory.Build.targets | 12 +- .../PointTests.cs | 24 + .../RectangleTests.cs | 26 + .../Synercoding.Primitives.Tests/SizeTests.cs | 24 + .../SpacingTests.cs | 27 ++ .../Synercoding.Primitives.Tests/UnitTests.cs | 35 ++ .../ValueTests.cs | 71 +++ 26 files changed, 595 insertions(+), 679 deletions(-) create mode 100644 src/Synercoding.Primitives/JsonConverters/PointJsonConverter.cs create mode 100644 src/Synercoding.Primitives/JsonConverters/RectangleJsonConverter.cs create mode 100644 src/Synercoding.Primitives/JsonConverters/SizeJsonConverter.cs create mode 100644 src/Synercoding.Primitives/JsonConverters/SpacingJsonConverter.cs create mode 100644 src/Synercoding.Primitives/JsonConverters/UnitJsonConverter.cs create mode 100644 src/Synercoding.Primitives/JsonConverters/ValueJsonConverter.cs delete mode 100644 src/Synercoding.Primitives/System/HashCode.cs create mode 100644 tests/Synercoding.Primitives.Tests/UnitTests.cs diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index ef84bcf..41a1d8b 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -33,7 +33,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 6.0.x + dotnet-version: 8.0.x - name: Restore run: dotnet restore - name: Build @@ -73,7 +73,7 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 6.0.x + dotnet-version: 8.0.x - name: Create Release NuGet package run: | arrTag=(${GITHUB_REF//\// }) diff --git a/Directory.Build.props b/Directory.Build.props index 3ac1d84..9721898 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -35,65 +35,12 @@ https://github.com/synercoder/Primitives/ https://api.nuget.org/v3/index.json; - https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json; true - 10.0 + 12.0 - - - - - - $(DefineConstants);SUPPORTS_EXTENDED_INTRINSICS - $(DefineConstants);SUPPORTS_CODECOVERAGE - - - - - $(DefineConstants);SUPPORTS_EXTENDED_INTRINSICS - $(DefineConstants);SUPPORTS_CODECOVERAGE - - - - - $(DefineConstants);SUPPORTS_MATHF - $(DefineConstants);SUPPORTS_HASHCODE - $(DefineConstants);SUPPORTS_SPAN_STREAM - $(DefineConstants);SUPPORTS_ENCODING_STRING - $(DefineConstants);SUPPORTS_CODECOVERAGE - $(DefineConstants);SUPPORTS_CREATESPAN - - - - - - $(DefineConstants);SUPPORTS_MATHF - $(DefineConstants);SUPPORTS_HASHCODE - $(DefineConstants);SUPPORTS_EXTENDED_INTRINSICS - $(DefineConstants);SUPPORTS_SPAN_STREAM - $(DefineConstants);SUPPORTS_ENCODING_STRING - $(DefineConstants);SUPPORTS_RUNTIME_INTRINSICS - $(DefineConstants);SUPPORTS_CODECOVERAGE - $(DefineConstants);SUPPORTS_HOTPATH - $(DefineConstants);SUPPORTS_CREATESPAN - $(DefineConstants);SUPPORTS_BITOPERATIONS - - - - diff --git a/src/Directory.Build.props b/src/Directory.Build.props index ccbf612..0c9b1de 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -2,7 +2,7 @@ - net48;netcoreapp3.1;netstandard1.6;netstandard2.0;netstandard2.1;net5.0;net6.0 + net8.0 $(MSBuildAllProjects);$(MSBuildThisFileDirectory)..\Directory.Build.props src diff --git a/src/Synercoding.Primitives/JsonConverters/PointJsonConverter.cs b/src/Synercoding.Primitives/JsonConverters/PointJsonConverter.cs new file mode 100644 index 0000000..7208fcb --- /dev/null +++ b/src/Synercoding.Primitives/JsonConverters/PointJsonConverter.cs @@ -0,0 +1,57 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Synercoding.Primitives.JsonConverters; + +public class PointJsonConverter : JsonConverter +{ + public static PointJsonConverter Instance { get; } = new(); + + public override Point Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + var textValue = reader.GetString() ?? throw new JsonException(); + + if (Point.TryParse(textValue, out var value)) + return value; + + throw new JsonException(); + } + + if (reader.TokenType != JsonTokenType.StartObject) + throw new JsonException(); + + var complexPoint = default(Point); + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + return complexPoint; + + if (reader.TokenType == JsonTokenType.PropertyName) + { + string propertyName = reader.GetString() ?? throw new JsonException(); + reader.Read(); + switch (propertyName) + { + case nameof(Point.X): + Value width = ValueJsonConverter.Instance.Read(ref reader, typeof(Value), options); + complexPoint = complexPoint with { X = width }; + break; + case nameof(Point.Y): + Value height = ValueJsonConverter.Instance.Read(ref reader, typeof(Value), options); + complexPoint = complexPoint with { Y = height }; + break; + } + } + } + + throw new JsonException(); + } + + public override void Write(Utf8JsonWriter writer, Point value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString()); + } +} \ No newline at end of file diff --git a/src/Synercoding.Primitives/JsonConverters/RectangleJsonConverter.cs b/src/Synercoding.Primitives/JsonConverters/RectangleJsonConverter.cs new file mode 100644 index 0000000..e860b27 --- /dev/null +++ b/src/Synercoding.Primitives/JsonConverters/RectangleJsonConverter.cs @@ -0,0 +1,65 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Synercoding.Primitives.JsonConverters; + +public class RectangleJsonConverter : JsonConverter +{ + public static RectangleJsonConverter Instance { get; } = new(); + + public override Rectangle Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + var textValue = reader.GetString() ?? throw new JsonException(); + + if (Rectangle.TryParse(textValue, out var value)) + return value; + + throw new JsonException(); + } + + if (reader.TokenType != JsonTokenType.StartObject) + throw new JsonException(); + + var complexRectangle = default(Rectangle); + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + return complexRectangle; + + if (reader.TokenType == JsonTokenType.PropertyName) + { + string propertyName = reader.GetString() ?? throw new JsonException(); + reader.Read(); + switch (propertyName) + { + case nameof(Rectangle.LLX): + Value llx = ValueJsonConverter.Instance.Read(ref reader, typeof(Value), options); + complexRectangle = complexRectangle with { LLX = llx }; + break; + case nameof(Rectangle.LLY): + Value lly = ValueJsonConverter.Instance.Read(ref reader, typeof(Value), options); + complexRectangle = complexRectangle with { LLY = lly }; + break; + case nameof(Rectangle.URX): + Value urx = ValueJsonConverter.Instance.Read(ref reader, typeof(Value), options); + complexRectangle = complexRectangle with { URX = urx }; + break; + case nameof(Rectangle.URY): + Value ury = ValueJsonConverter.Instance.Read(ref reader, typeof(Value), options); + complexRectangle = complexRectangle with { URY = ury }; + break; + } + } + } + + throw new JsonException(); + } + + public override void Write(Utf8JsonWriter writer, Rectangle value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString()); + } +} diff --git a/src/Synercoding.Primitives/JsonConverters/SizeJsonConverter.cs b/src/Synercoding.Primitives/JsonConverters/SizeJsonConverter.cs new file mode 100644 index 0000000..2d0ae51 --- /dev/null +++ b/src/Synercoding.Primitives/JsonConverters/SizeJsonConverter.cs @@ -0,0 +1,57 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Synercoding.Primitives.JsonConverters; + +public class SizeJsonConverter : JsonConverter +{ + public static SizeJsonConverter Instance { get; } = new(); + + public override Size Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + var textValue = reader.GetString() ?? throw new JsonException(); + + if (Size.TryParse(textValue, out var value)) + return value; + + throw new JsonException(); + } + + if (reader.TokenType != JsonTokenType.StartObject) + throw new JsonException(); + + var complexSize = default(Size); + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + return complexSize; + + if (reader.TokenType == JsonTokenType.PropertyName) + { + string propertyName = reader.GetString() ?? throw new JsonException(); + reader.Read(); + switch (propertyName) + { + case nameof(Size.Width): + Value width = ValueJsonConverter.Instance.Read(ref reader, typeof(Value), options); + complexSize = complexSize with { Width = width }; + break; + case nameof(Size.Height): + Value height = ValueJsonConverter.Instance.Read(ref reader, typeof(Value), options); + complexSize = complexSize with { Height = height }; + break; + } + } + } + + throw new JsonException(); + } + + public override void Write(Utf8JsonWriter writer, Size value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString()); + } +} diff --git a/src/Synercoding.Primitives/JsonConverters/SpacingJsonConverter.cs b/src/Synercoding.Primitives/JsonConverters/SpacingJsonConverter.cs new file mode 100644 index 0000000..3f82f4d --- /dev/null +++ b/src/Synercoding.Primitives/JsonConverters/SpacingJsonConverter.cs @@ -0,0 +1,65 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Synercoding.Primitives.JsonConverters; + +public class SpacingJsonConverter : JsonConverter +{ + public static SpacingJsonConverter Instance { get; } = new(); + + public override Spacing Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + var textValue = reader.GetString() ?? throw new JsonException(); + + if (Spacing.TryParse(textValue, out var value)) + return value; + + throw new JsonException(); + } + + if (reader.TokenType != JsonTokenType.StartObject) + throw new JsonException(); + + var complexSpacing = default(Spacing); + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + return complexSpacing; + + if (reader.TokenType == JsonTokenType.PropertyName) + { + string propertyName = reader.GetString() ?? throw new JsonException(); + reader.Read(); + switch (propertyName) + { + case nameof(Spacing.Left): + Value left = ValueJsonConverter.Instance.Read(ref reader, typeof(Value), options); + complexSpacing = complexSpacing with { Left = left }; + break; + case nameof(Spacing.Right): + Value right = ValueJsonConverter.Instance.Read(ref reader, typeof(Value), options); + complexSpacing = complexSpacing with { Right = right }; + break; + case nameof(Spacing.Top): + Value top = ValueJsonConverter.Instance.Read(ref reader, typeof(Value), options); + complexSpacing = complexSpacing with { Top = top }; + break; + case nameof(Spacing.Bottom): + Value bottom = ValueJsonConverter.Instance.Read(ref reader, typeof(Value), options); + complexSpacing = complexSpacing with { Bottom = bottom }; + break; + } + } + } + + throw new JsonException(); + } + + public override void Write(Utf8JsonWriter writer, Spacing value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString()); + } +} diff --git a/src/Synercoding.Primitives/JsonConverters/UnitJsonConverter.cs b/src/Synercoding.Primitives/JsonConverters/UnitJsonConverter.cs new file mode 100644 index 0000000..fc5f0f0 --- /dev/null +++ b/src/Synercoding.Primitives/JsonConverters/UnitJsonConverter.cs @@ -0,0 +1,29 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Synercoding.Primitives.JsonConverters; + +public class UnitJsonConverter : JsonConverter +{ + public static UnitJsonConverter Instance { get; } = new(); + + public override Unit Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.String) + throw new JsonException(); + + var textValue = reader.GetString() ?? throw new JsonException(); + + if (Unit.TryParse(textValue, out var unit)) + return unit; + + throw new JsonException(); + } + + public override void Write(Utf8JsonWriter writer, Unit value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString()); + } +} + diff --git a/src/Synercoding.Primitives/JsonConverters/ValueJsonConverter.cs b/src/Synercoding.Primitives/JsonConverters/ValueJsonConverter.cs new file mode 100644 index 0000000..b632d1d --- /dev/null +++ b/src/Synercoding.Primitives/JsonConverters/ValueJsonConverter.cs @@ -0,0 +1,57 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Synercoding.Primitives.JsonConverters; + +public class ValueJsonConverter : JsonConverter +{ + public static ValueJsonConverter Instance { get; } = new(); + + public override Value Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + var textValue = reader.GetString() ?? throw new JsonException(); + + if (Value.TryParse(textValue, out var value)) + return value; + + throw new JsonException(); + } + + if (reader.TokenType != JsonTokenType.StartObject) + throw new JsonException(); + + var complexValue = default(Value); + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + return complexValue; + + if (reader.TokenType == JsonTokenType.PropertyName) + { + string propertyName = reader.GetString() ?? throw new JsonException(); + reader.Read(); + switch (propertyName) + { + case nameof(Value.Raw): + double raw = reader.GetDouble(); + complexValue = complexValue with { Raw = raw }; + break; + case nameof(Value.Unit): + Unit unit = UnitJsonConverter.Instance.Read(ref reader, typeof(Unit), options); + complexValue = complexValue with { Unit = unit }; + break; + } + } + } + + throw new JsonException(); + } + + public override void Write(Utf8JsonWriter writer, Value value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString()); + } +} diff --git a/src/Synercoding.Primitives/PackageDetails.props b/src/Synercoding.Primitives/PackageDetails.props index 14af0c7..8beb102 100644 --- a/src/Synercoding.Primitives/PackageDetails.props +++ b/src/Synercoding.Primitives/PackageDetails.props @@ -10,7 +10,7 @@ Synercoding.Primitives Synercoding.Primitives Primitives with units attached (think mm, cm, in, px) that can be used for 2D graphics. - - Fix default value of unitdesignation + Limit to latest .NET version (8) and add System.Json serialization support. \ No newline at end of file diff --git a/src/Synercoding.Primitives/Point.cs b/src/Synercoding.Primitives/Point.cs index 741406f..e2e1f57 100644 --- a/src/Synercoding.Primitives/Point.cs +++ b/src/Synercoding.Primitives/Point.cs @@ -1,5 +1,6 @@ using Synercoding.Primitives.Abstract; using System; +using System.Text.Json.Serialization; using System.Text.RegularExpressions; namespace Synercoding.Primitives; @@ -7,7 +8,8 @@ namespace Synercoding.Primitives; /// /// A value type representing a point in a 2D space. /// -public readonly struct Point : IConvertable, IEquatable +[JsonConverter(typeof(JsonConverters.PointJsonConverter))] +public readonly record struct Point : IConvertable, IEquatable { /// /// Constructor for a . @@ -52,12 +54,12 @@ public static Point Origin /// /// The X coordinate. /// - public Value X { get; } + public Value X { get; init; } /// /// The Y coordinate. /// - public Value Y { get; } + public Value Y { get; init; } /// public Point ConvertTo(Unit unit) @@ -71,10 +73,6 @@ public Point ConvertTo(Unit unit) public override int GetHashCode() => HashCode.Combine(X, Y); - /// - public override bool Equals(object? obj) - => obj is Point unit && Equals(unit); - /// public bool Equals(Point other) { @@ -89,24 +87,6 @@ public bool Equals(Point other) public override string ToString() => $"X: {X}, Y: {Y}"; - /// - /// Returns a value that indicates whether two specified values are equal. - /// - /// The first value to compare. - /// The second value to compare. - /// true if left and right are equal; otherwise, false. - public static bool operator ==(Point left, Point right) - => left.Equals(right); - - /// - /// Returns a value that indicates whether two specified values are not equal. - /// - /// The first value to compare. - /// The second value to compare. - /// true if left and right are not equal; otherwise, false. - public static bool operator !=(Point left, Point right) - => !( left == right ); - /// /// Parse a string into a /// diff --git a/src/Synercoding.Primitives/Rectangle.cs b/src/Synercoding.Primitives/Rectangle.cs index 9659aa9..76f00f0 100644 --- a/src/Synercoding.Primitives/Rectangle.cs +++ b/src/Synercoding.Primitives/Rectangle.cs @@ -1,5 +1,6 @@ using Synercoding.Primitives.Abstract; using System; +using System.Text.Json.Serialization; using System.Text.RegularExpressions; namespace Synercoding.Primitives; @@ -7,7 +8,8 @@ namespace Synercoding.Primitives; /// /// Value type representing a rectangle. /// -public readonly struct Rectangle : IConvertable, IEquatable +[JsonConverter(typeof(JsonConverters.RectangleJsonConverter))] +public readonly record struct Rectangle : IConvertable, IEquatable { /// /// Constructor for . @@ -88,22 +90,22 @@ public static Rectangle Zero /// /// The lower left x coordinate. /// - public Value LLX { get; } + public Value LLX { get; init; } /// /// The lower left y coordinate. /// - public Value LLY { get; } + public Value LLY { get; init; } /// /// The upper right x coordinate. /// - public Value URX { get; } + public Value URX { get; init; } /// /// The upper right y coordinate. /// - public Value URY { get; } + public Value URY { get; init; } /// /// The width of this . @@ -143,10 +145,6 @@ public Rectangle ConvertTo(Unit unit) public override int GetHashCode() => HashCode.Combine(LLX, LLY, URX, URY); - /// - public override bool Equals(object? obj) - => obj is Rectangle unit && Equals(unit); - /// public bool Equals(Rectangle other) { @@ -163,24 +161,6 @@ public bool Equals(Rectangle other) public override string ToString() => $"LLX: {LLX}, LLY: {LLY}, URX: {URX}, URY: {URY}"; - /// - /// Returns a value that indicates whether two specified values are equal. - /// - /// The first value to compare. - /// The second value to compare. - /// true if left and right are equal; otherwise, false. - public static bool operator ==(Rectangle left, Rectangle right) - => left.Equals(right); - - /// - /// Returns a value that indicates whether two specified values are not equal. - /// - /// The first value to compare. - /// The second value to compare. - /// true if left and right are not equal; otherwise, false. - public static bool operator !=(Rectangle left, Rectangle right) - => !( left == right ); - /// /// Parse a string into a /// diff --git a/src/Synercoding.Primitives/Size.cs b/src/Synercoding.Primitives/Size.cs index b31efc2..d056553 100644 --- a/src/Synercoding.Primitives/Size.cs +++ b/src/Synercoding.Primitives/Size.cs @@ -1,5 +1,6 @@ using Synercoding.Primitives.Abstract; using System; +using System.Text.Json.Serialization; using System.Text.RegularExpressions; namespace Synercoding.Primitives; @@ -7,7 +8,8 @@ namespace Synercoding.Primitives; /// /// A value type representing a size using and . /// -public readonly struct Size : IConvertable, IEquatable +[JsonConverter(typeof(JsonConverters.SizeJsonConverter))] +public readonly record struct Size : IConvertable, IEquatable { /// /// Constructor for a . @@ -52,12 +54,12 @@ public static Size Empty /// /// The width property. /// - public Value Width { get; } + public Value Width { get; init; } /// /// The height property. /// - public Value Height { get; } + public Value Height { get; init; } /// /// The rotated version of this . @@ -92,10 +94,6 @@ public Size ConvertTo(Unit unit) public override int GetHashCode() => HashCode.Combine(Width, Height); - /// - public override bool Equals(object? obj) - => obj is Size unit && Equals(unit); - /// public bool Equals(Size other) { @@ -110,24 +108,6 @@ public bool Equals(Size other) public override string ToString() => $"W: {Width}, H: {Height}"; - /// - /// Returns a value that indicates whether two specified values are equal. - /// - /// The first value to compare. - /// The second value to compare. - /// true if left and right are equal; otherwise, false. - public static bool operator ==(Size left, Size right) - => left.Equals(right); - - /// - /// Returns a value that indicates whether two specified values are not equal. - /// - /// The first value to compare. - /// The second value to compare. - /// true if left and right are not equal; otherwise, false. - public static bool operator !=(Size left, Size right) - => !( left == right ); - /// /// Parse a string into a /// diff --git a/src/Synercoding.Primitives/Spacing.cs b/src/Synercoding.Primitives/Spacing.cs index 74b7b0f..bd84858 100644 --- a/src/Synercoding.Primitives/Spacing.cs +++ b/src/Synercoding.Primitives/Spacing.cs @@ -1,5 +1,6 @@ using Synercoding.Primitives.Abstract; using System; +using System.Text.Json.Serialization; using System.Text.RegularExpressions; namespace Synercoding.Primitives; @@ -7,7 +8,8 @@ namespace Synercoding.Primitives; /// /// Value type representing spacing in or around an object. /// -public readonly struct Spacing : IConvertable, IEquatable +[JsonConverter(typeof(JsonConverters.SpacingJsonConverter))] +public readonly record struct Spacing : IConvertable, IEquatable { /// /// Constructor for . @@ -81,22 +83,22 @@ public static Spacing Nothing /// /// The amount of spacing on the left side. /// - public Value Left { get; } + public Value Left { get; init; } /// /// The amount of spacing on the top side. /// - public Value Top { get; } + public Value Top { get; init; } /// /// The amount of spacing on the right side. /// - public Value Right { get; } + public Value Right { get; init; } /// /// The amount of spacing on the bottom side. /// - public Value Bottom { get; } + public Value Bottom { get; init; } /// public Spacing ConvertTo(Unit unit) @@ -114,11 +116,13 @@ public override int GetHashCode() => HashCode.Combine(Left, Top, Right, Bottom); /// - public override bool Equals(object? obj) - => obj is Spacing unit && Equals(unit); + public override string ToString() + { + if (Left == Top && Top == Right && Right == Bottom) + return $"All: {Left}"; - /// - public override string ToString() => $"L: {Left}, T: {Top}, R: {Right}, B: {Bottom}"; + return $"L: {Left}, T: {Top}, R: {Right}, B: {Bottom}"; + } /// public bool Equals(Spacing other) @@ -132,24 +136,6 @@ public bool Equals(Spacing other) && a.Bottom == b.Bottom; } - /// - /// Returns a value that indicates whether two specified values are equal. - /// - /// The first value to compare. - /// The second value to compare. - /// true if left and right are equal; otherwise, false. - public static bool operator ==(Spacing left, Spacing right) - => left.Equals(right); - - /// - /// Returns a value that indicates whether two specified values are not equal. - /// - /// The first value to compare. - /// The second value to compare. - /// true if left and right are not equal; otherwise, false. - public static bool operator !=(Spacing left, Spacing right) - => !( left == right ); - /// /// Parse a string into a /// @@ -176,6 +162,12 @@ public static bool TryParse(string s, out Spacing spacing) s = s.Trim(); + if (s.StartsWith("All:") && Value.TryParse(s.Substring(4).TrimStart(), out var all)) + { + spacing = new Spacing(all); + return true; + } + var match = Regex.Match(s, "^L: ?(.+), ?T: ?(.+), ?R: ?(.+), ?B: ?(.+)$"); if (match.Success && match.Groups.Count == 5) { diff --git a/src/Synercoding.Primitives/Synercoding.Primitives.csproj b/src/Synercoding.Primitives/Synercoding.Primitives.csproj index aefbd57..444a59f 100644 --- a/src/Synercoding.Primitives/Synercoding.Primitives.csproj +++ b/src/Synercoding.Primitives/Synercoding.Primitives.csproj @@ -1,17 +1,5 @@ - - enable - - - - - - - - - - diff --git a/src/Synercoding.Primitives/System/HashCode.cs b/src/Synercoding.Primitives/System/HashCode.cs deleted file mode 100644 index aad27d7..0000000 --- a/src/Synercoding.Primitives/System/HashCode.cs +++ /dev/null @@ -1,446 +0,0 @@ -// -// SOURCE: https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/HashCode.cs - -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -/* -The xxHash32 implementation is based on the code published by Yann Collet: -https://raw.githubusercontent.com/Cyan4973/xxHash/5c174cfa4e45a42f94082dc0d4539b39696afea1/xxhash.c - xxHash - Fast Hash algorithm - Copyright (C) 2012-2016, Yann Collet - - BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - You can contact the author at : - - xxHash homepage: http://www.xxhash.com - - xxHash source repository : https://github.com/Cyan4973/xxHash -*/ - -#if SUPPORTS_HASHCODE -using System.Runtime.CompilerServices; - -[assembly: TypeForwardedTo(typeof(System.HashCode))] -#else -using System.Collections.Generic; -using System.ComponentModel; -using System.Runtime.CompilerServices; -using System.Security.Cryptography; - -namespace System -{ - // xxHash32 is used for the hash code. - // https://github.com/Cyan4973/xxHash - internal struct HashCode - { -#pragma warning disable SA1311 // Static readonly fields should begin with upper-case letter - private static readonly uint s_seed = GenerateGlobalSeed(); -#pragma warning restore SA1311 // Static readonly fields should begin with upper-case letter - - private const uint Prime1 = 2654435761U; - private const uint Prime2 = 2246822519U; - private const uint Prime3 = 3266489917U; - private const uint Prime4 = 668265263U; - private const uint Prime5 = 374761393U; - - private uint _v1, _v2, _v3, _v4; - private uint _queue1, _queue2, _queue3; - private uint _length; - - private static uint GenerateGlobalSeed() - { - byte[] data = new byte[4]; - - using (var rng = RandomNumberGenerator.Create()) - { - rng.GetBytes(data); - } - - return BitConverter.ToUInt32(data, 0); - } - - public static int Combine(T1 value1) - { - // Provide a way of diffusing bits from something with a limited - // input hash space. For example, many enums only have a few - // possible hashes, only using the bottom few bits of the code. Some - // collections are built on the assumption that hashes are spread - // over a larger space, so diffusing the bits may help the - // collection work more efficiently. - - var hc1 = (uint)(value1?.GetHashCode() ?? 0); - - uint hash = MixEmptyState(); - hash += 4; - - hash = QueueRound(hash, hc1); - - hash = MixFinal(hash); - return (int)hash; - } - - public static int Combine(T1 value1, T2 value2) - { - var hc1 = (uint)(value1?.GetHashCode() ?? 0); - var hc2 = (uint)(value2?.GetHashCode() ?? 0); - - uint hash = MixEmptyState(); - hash += 8; - - hash = QueueRound(hash, hc1); - hash = QueueRound(hash, hc2); - - hash = MixFinal(hash); - return (int)hash; - } - - public static int Combine(T1 value1, T2 value2, T3 value3) - { - var hc1 = (uint)(value1?.GetHashCode() ?? 0); - var hc2 = (uint)(value2?.GetHashCode() ?? 0); - var hc3 = (uint)(value3?.GetHashCode() ?? 0); - - uint hash = MixEmptyState(); - hash += 12; - - hash = QueueRound(hash, hc1); - hash = QueueRound(hash, hc2); - hash = QueueRound(hash, hc3); - - hash = MixFinal(hash); - return (int)hash; - } - - public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4) - { - var hc1 = (uint)(value1?.GetHashCode() ?? 0); - var hc2 = (uint)(value2?.GetHashCode() ?? 0); - var hc3 = (uint)(value3?.GetHashCode() ?? 0); - var hc4 = (uint)(value4?.GetHashCode() ?? 0); - - Initialize(out uint v1, out uint v2, out uint v3, out uint v4); - - v1 = Round(v1, hc1); - v2 = Round(v2, hc2); - v3 = Round(v3, hc3); - v4 = Round(v4, hc4); - - uint hash = MixState(v1, v2, v3, v4); - hash += 16; - - hash = MixFinal(hash); - return (int)hash; - } - - public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5) - { - var hc1 = (uint)(value1?.GetHashCode() ?? 0); - var hc2 = (uint)(value2?.GetHashCode() ?? 0); - var hc3 = (uint)(value3?.GetHashCode() ?? 0); - var hc4 = (uint)(value4?.GetHashCode() ?? 0); - var hc5 = (uint)(value5?.GetHashCode() ?? 0); - - Initialize(out uint v1, out uint v2, out uint v3, out uint v4); - - v1 = Round(v1, hc1); - v2 = Round(v2, hc2); - v3 = Round(v3, hc3); - v4 = Round(v4, hc4); - - uint hash = MixState(v1, v2, v3, v4); - hash += 20; - - hash = QueueRound(hash, hc5); - - hash = MixFinal(hash); - return (int)hash; - } - - public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6) - { - var hc1 = (uint)(value1?.GetHashCode() ?? 0); - var hc2 = (uint)(value2?.GetHashCode() ?? 0); - var hc3 = (uint)(value3?.GetHashCode() ?? 0); - var hc4 = (uint)(value4?.GetHashCode() ?? 0); - var hc5 = (uint)(value5?.GetHashCode() ?? 0); - var hc6 = (uint)(value6?.GetHashCode() ?? 0); - - Initialize(out uint v1, out uint v2, out uint v3, out uint v4); - - v1 = Round(v1, hc1); - v2 = Round(v2, hc2); - v3 = Round(v3, hc3); - v4 = Round(v4, hc4); - - uint hash = MixState(v1, v2, v3, v4); - hash += 24; - - hash = QueueRound(hash, hc5); - hash = QueueRound(hash, hc6); - - hash = MixFinal(hash); - return (int)hash; - } - - public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7) - { - var hc1 = (uint)(value1?.GetHashCode() ?? 0); - var hc2 = (uint)(value2?.GetHashCode() ?? 0); - var hc3 = (uint)(value3?.GetHashCode() ?? 0); - var hc4 = (uint)(value4?.GetHashCode() ?? 0); - var hc5 = (uint)(value5?.GetHashCode() ?? 0); - var hc6 = (uint)(value6?.GetHashCode() ?? 0); - var hc7 = (uint)(value7?.GetHashCode() ?? 0); - - Initialize(out uint v1, out uint v2, out uint v3, out uint v4); - - v1 = Round(v1, hc1); - v2 = Round(v2, hc2); - v3 = Round(v3, hc3); - v4 = Round(v4, hc4); - - uint hash = MixState(v1, v2, v3, v4); - hash += 28; - - hash = QueueRound(hash, hc5); - hash = QueueRound(hash, hc6); - hash = QueueRound(hash, hc7); - - hash = MixFinal(hash); - return (int)hash; - } - - public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8) - { - var hc1 = (uint)(value1?.GetHashCode() ?? 0); - var hc2 = (uint)(value2?.GetHashCode() ?? 0); - var hc3 = (uint)(value3?.GetHashCode() ?? 0); - var hc4 = (uint)(value4?.GetHashCode() ?? 0); - var hc5 = (uint)(value5?.GetHashCode() ?? 0); - var hc6 = (uint)(value6?.GetHashCode() ?? 0); - var hc7 = (uint)(value7?.GetHashCode() ?? 0); - var hc8 = (uint)(value8?.GetHashCode() ?? 0); - - Initialize(out uint v1, out uint v2, out uint v3, out uint v4); - - v1 = Round(v1, hc1); - v2 = Round(v2, hc2); - v3 = Round(v3, hc3); - v4 = Round(v4, hc4); - - v1 = Round(v1, hc5); - v2 = Round(v2, hc6); - v3 = Round(v3, hc7); - v4 = Round(v4, hc8); - - uint hash = MixState(v1, v2, v3, v4); - hash += 32; - - hash = MixFinal(hash); - return (int)hash; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Rol(uint value, int count) - => (value << count) | (value >> (32 - count)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Initialize(out uint v1, out uint v2, out uint v3, out uint v4) - { - v1 = s_seed + Prime1 + Prime2; - v2 = s_seed + Prime2; - v3 = s_seed; - v4 = s_seed - Prime1; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Round(uint hash, uint input) - { - hash += input * Prime2; - hash = Rol(hash, 13); - hash *= Prime1; - return hash; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint QueueRound(uint hash, uint queuedValue) - { - hash += queuedValue * Prime3; - return Rol(hash, 17) * Prime4; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint MixState(uint v1, uint v2, uint v3, uint v4) - { - return Rol(v1, 1) + Rol(v2, 7) + Rol(v3, 12) + Rol(v4, 18); - } - - private static uint MixEmptyState() - { - return s_seed + Prime5; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint MixFinal(uint hash) - { - hash ^= hash >> 15; - hash *= Prime2; - hash ^= hash >> 13; - hash *= Prime3; - hash ^= hash >> 16; - return hash; - } - - public void Add(T value) - { - Add(value?.GetHashCode() ?? 0); - } - - public void Add(T value, IEqualityComparer comparer) - { - Add(comparer != null ? comparer.GetHashCode(value) : (value?.GetHashCode() ?? 0)); - } - - private void Add(int value) - { - // The original xxHash works as follows: - // 0. Initialize immediately. We can't do this in a struct (no - // default ctor). - // 1. Accumulate blocks of length 16 (4 uints) into 4 accumulators. - // 2. Accumulate remaining blocks of length 4 (1 uint) into the - // hash. - // 3. Accumulate remaining blocks of length 1 into the hash. - - // There is no need for #3 as this type only accepts ints. _queue1, - // _queue2 and _queue3 are basically a buffer so that when - // ToHashCode is called we can execute #2 correctly. - - // We need to initialize the xxHash32 state (_v1 to _v4) lazily (see - // #0) nd the last place that can be done if you look at the - // original code is just before the first block of 16 bytes is mixed - // in. The xxHash32 state is never used for streams containing fewer - // than 16 bytes. - - // To see what's really going on here, have a look at the Combine - // methods. - - var val = (uint)value; - - // Storing the value of _length locally shaves of quite a few bytes - // in the resulting machine code. - uint previousLength = _length++; - uint position = previousLength % 4; - - // Switch can't be inlined. - - if (position == 0) - _queue1 = val; - else if (position == 1) - _queue2 = val; - else if (position == 2) - _queue3 = val; - else // position == 3 - { - if (previousLength == 3) - Initialize(out _v1, out _v2, out _v3, out _v4); - - _v1 = Round(_v1, _queue1); - _v2 = Round(_v2, _queue2); - _v3 = Round(_v3, _queue3); - _v4 = Round(_v4, val); - } - } - - public int ToHashCode() - { - // Storing the value of _length locally shaves of quite a few bytes - // in the resulting machine code. - uint length = _length; - - // position refers to the *next* queue position in this method, so - // position == 1 means that _queue1 is populated; _queue2 would have - // been populated on the next call to Add. - uint position = length % 4; - - // If the length is less than 4, _v1 to _v4 don't contain anything - // yet. xxHash32 treats this differently. - - uint hash = length < 4 ? MixEmptyState() : MixState(_v1, _v2, _v3, _v4); - - // _length is incremented once per Add(Int32) and is therefore 4 - // times too small (xxHash length is in bytes, not ints). - - hash += length * 4; - - // Mix what remains in the queue - - // Switch can't be inlined right now, so use as few branches as - // possible by manually excluding impossible scenarios (position > 1 - // is always false if position is not > 0). - if (position > 0) - { - hash = QueueRound(hash, _queue1); - if (position > 1) - { - hash = QueueRound(hash, _queue2); - if (position > 2) - hash = QueueRound(hash, _queue3); - } - } - - hash = MixFinal(hash); - return (int)hash; - } - -#pragma warning disable 0809 - - // Obsolete member 'memberA' overrides non-obsolete member 'memberB'. - // Disallowing GetHashCode and Equals is by design - - // * We decided to not override GetHashCode() to produce the hash code - // as this would be weird, both naming-wise as well as from a - // behavioral standpoint (GetHashCode() should return the object's - // hash code, not the one being computed). - - // * Even though ToHashCode() can be called safely multiple times on - // this implementation, it is not part of the contract. If the - // implementation has to change in the future we don't want to worry - // about people who might have incorrectly used this type. - - [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code.", error: true)] - [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetHashCode() => throw new NotSupportedException("Equality not supported"); - - [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes.", error: true)] - [EditorBrowsable(EditorBrowsableState.Never)] - public override bool Equals(object obj) => throw new NotSupportedException("Equality not supported"); -#pragma warning restore 0809 - } -} -#endif - -#pragma warning restore SA1636, SA1600, SA1503, SA1202, SA1101, SA1132, SA1309, SA1520, SA1108, SA1203, SA1028, SA1512, SA1308 \ No newline at end of file diff --git a/src/Synercoding.Primitives/Unit.cs b/src/Synercoding.Primitives/Unit.cs index 472ffb1..14a5f65 100644 --- a/src/Synercoding.Primitives/Unit.cs +++ b/src/Synercoding.Primitives/Unit.cs @@ -1,5 +1,6 @@ using Synercoding.Primitives.Extensions; using System; +using System.Text.Json.Serialization; using System.Text.RegularExpressions; namespace Synercoding.Primitives; @@ -7,7 +8,8 @@ namespace Synercoding.Primitives; /// /// Value type representing an . /// -public readonly struct Unit : IEquatable +[JsonConverter(typeof(JsonConverters.UnitJsonConverter))] +public readonly record struct Unit : IEquatable { private Unit(double perInch, UnitDesignation unitDesignation) { @@ -23,12 +25,12 @@ private Unit(double perInch, UnitDesignation unitDesignation) /// /// Value representing how many of this are in an inch. /// - public double PerInch { get; } + public double PerInch { get; init; } /// /// The designation of the unit. /// - public UnitDesignation Designation { get; } + public UnitDesignation Designation { get; init; } /// public bool Equals(Unit other) @@ -44,29 +46,9 @@ public bool Equals(Unit other) public override string ToString() => $"{Designation.Shortform()} ({PerInch} per inch)"; - /// - public override bool Equals(object? obj) - => obj is Unit unit && Equals(unit); - /// public override int GetHashCode() => HashCode.Combine(PerInch, Designation); - /// - /// Returns a value that indicates whether two specified values are equal. - /// - /// The first value to compare. - /// The second value to compare. - /// true if left and right are equal; otherwise, false. - public static bool operator ==(Unit left, Unit right) => left.Equals(right); - - /// - /// Returns a value that indicates whether two specified values are not equal. - /// - /// The first value to compare. - /// The second value to compare. - /// true if left and right are not equal; otherwise, false. - public static bool operator !=(Unit left, Unit right) => !( left == right ); - /// /// Unit for millimeters. /// @@ -135,7 +117,7 @@ public static bool TryParse(string s, out Unit unit) { s = s.Trim(); - if (Enum.TryParse(s, true, out var designation)) + if (Enum.TryParse(s, true, out var designation) && designation != UnitDesignation.Pixels) { unit = FromDesignation(designation); return true; @@ -156,7 +138,7 @@ public static bool TryParse(string s, out Unit unit) if(unit == default && s.StartsWith("dpi")) { - var match = Regex.Match(s, "^dpi\\(([0-9]+)\\)$"); + var match = Regex.Match(s, "^dpi\\(([1-9][0-9]*(?:\\.[0-9]+)?)\\)$"); if(match.Success && match.Groups.Count == 2) { var dpi = int.Parse(match.Groups[1].Value); diff --git a/src/Synercoding.Primitives/Value.cs b/src/Synercoding.Primitives/Value.cs index 8280d40..2969d56 100644 --- a/src/Synercoding.Primitives/Value.cs +++ b/src/Synercoding.Primitives/Value.cs @@ -2,6 +2,7 @@ using Synercoding.Primitives.Extensions; using System; using System.Globalization; +using System.Text.Json.Serialization; namespace Synercoding.Primitives; @@ -9,7 +10,8 @@ namespace Synercoding.Primitives; /// Represents a double with a unit type attached. /// /// 3mm or 2 inches -public readonly struct Value : IConvertable, IComparable, IComparable, IEquatable +[JsonConverter(typeof(JsonConverters.ValueJsonConverter))] +public readonly record struct Value : IConvertable, IComparable, IComparable, IEquatable { private const int ROUND_DIGITS = 15; @@ -27,12 +29,12 @@ public Value(double value, Unit unit) /// /// The number part of the . /// - public double Raw { get; } + public double Raw { get; init; } /// /// The unit part of the . /// - public Unit Unit { get; } + public Unit Unit { get; init; } /// public Value ConvertTo(Unit unit) @@ -77,10 +79,6 @@ public override int GetHashCode() public override string ToString() => $"{Raw.ToString(CultureInfo.InvariantCulture)} {Unit.Designation.Shortform()}"; - /// - public override bool Equals(object? obj) - => obj is Value unit && Equals(unit); - /// public bool Equals(Value other) => CompareTo(other) == 0; @@ -90,24 +88,6 @@ public bool Equals(Value other) /// public static Value Zero => new Value(0, Unit.Inches); - /// - /// Returns a value that indicates whether two specified values are equal. - /// - /// The first value to compare. - /// The second value to compare. - /// true if left and right are equal; otherwise, false. - public static bool operator ==(Value left, Value right) - => left.Equals(right); - - /// - /// Returns a value that indicates whether two specified values are not equal. - /// - /// The first value to compare. - /// The second value to compare. - /// true if left and right are not equal; otherwise, false. - public static bool operator !=(Value left, Value right) - => !( left == right ); - /// /// Returns a value that indicates whether a specified value is less than another specified value. /// diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index f896a2f..a8d7f09 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -14,7 +14,7 @@ - net6.0 + net8.0 false diff --git a/tests/Directory.Build.targets b/tests/Directory.Build.targets index 272e157..dc35e9f 100644 --- a/tests/Directory.Build.targets +++ b/tests/Directory.Build.targets @@ -8,17 +8,13 @@ - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive + \ No newline at end of file diff --git a/tests/Synercoding.Primitives.Tests/PointTests.cs b/tests/Synercoding.Primitives.Tests/PointTests.cs index 50baf8c..4a82122 100644 --- a/tests/Synercoding.Primitives.Tests/PointTests.cs +++ b/tests/Synercoding.Primitives.Tests/PointTests.cs @@ -67,4 +67,28 @@ public static IEnumerable DataForToString_TryParse_Same_Value { new object[]{ new Point(1.23, 3.21, Unit.Millimeters) }, }; + + [Theory] + [MemberData(nameof(DataForConvert_From_Json_IsCorrect))] + public void Convert_From_Json_IsCorrect(string value, Point expected) + { + // Act + var result = System.Text.Json.JsonSerializer.Deserialize(value); + + // Assert + Assert.Equal(expected, result); + } + + public static IEnumerable DataForConvert_From_Json_IsCorrect + => new[] + { + new object[] + { + "{ \"X\": \"10mm\", \"Y\": \"20mm\" }", + new Point( + x: new Value(10, Unit.Millimeters), + y: new Value(20, Unit.Millimeters) + ), + } + }; } diff --git a/tests/Synercoding.Primitives.Tests/RectangleTests.cs b/tests/Synercoding.Primitives.Tests/RectangleTests.cs index c14687b..2b0398c 100644 --- a/tests/Synercoding.Primitives.Tests/RectangleTests.cs +++ b/tests/Synercoding.Primitives.Tests/RectangleTests.cs @@ -83,4 +83,30 @@ public static IEnumerable DataForToString_TryParse_Same_Value { new object[]{ new Rectangle(1.23, 3.21, 12.34, 43.21, Unit.Millimeters) }, }; + + [Theory] + [MemberData(nameof(DataForConvert_From_Json_IsCorrect))] + public void Convert_From_Json_IsCorrect(string value, Rectangle expected) + { + // Act + var result = System.Text.Json.JsonSerializer.Deserialize(value); + + // Assert + Assert.Equal(expected, result); + } + + public static IEnumerable DataForConvert_From_Json_IsCorrect + => new[] + { + new object[] + { + "{ \"LLX\": \"1mm\", \"LLY\": \"2mm\", \"URX\": \"3mm\", \"URY\": \"4mm\" }", + new Rectangle( + llx: new Value(1, Unit.Millimeters), + lly: new Value(2, Unit.Millimeters), + urx: new Value(3, Unit.Millimeters), + ury: new Value(4, Unit.Millimeters) + ), + }, + }; } diff --git a/tests/Synercoding.Primitives.Tests/SizeTests.cs b/tests/Synercoding.Primitives.Tests/SizeTests.cs index 7a2b479..3fd7434 100644 --- a/tests/Synercoding.Primitives.Tests/SizeTests.cs +++ b/tests/Synercoding.Primitives.Tests/SizeTests.cs @@ -67,4 +67,28 @@ public static IEnumerable DataForToString_TryParse_Same_Value { new object[]{ new Size(1.23, 3.21, Unit.Millimeters) }, }; + + [Theory] + [MemberData(nameof(DataForConvert_From_Json_IsCorrect))] + public void Convert_From_Json_IsCorrect(string value, Size expected) + { + // Act + var result = System.Text.Json.JsonSerializer.Deserialize(value); + + // Assert + Assert.Equal(expected, result); + } + + public static IEnumerable DataForConvert_From_Json_IsCorrect + => new[] + { + new object[] + { + "{ \"Width\": \"10mm\", \"Height\": \"20mm\" }", + new Size( + width: new Value(10, Unit.Millimeters), + height: new Value(20, Unit.Millimeters) + ), + } + }; } diff --git a/tests/Synercoding.Primitives.Tests/SpacingTests.cs b/tests/Synercoding.Primitives.Tests/SpacingTests.cs index 313cf25..74077d4 100644 --- a/tests/Synercoding.Primitives.Tests/SpacingTests.cs +++ b/tests/Synercoding.Primitives.Tests/SpacingTests.cs @@ -67,4 +67,31 @@ public static IEnumerable DataForToString_TryParse_Same_Value { new object[]{ new Spacing(1.23, 3.21, 12.34, 43.21, Unit.Millimeters) }, }; + + [Theory] + [MemberData(nameof(DataForConvert_From_Json_IsCorrect))] + public void Convert_From_Json_IsCorrect(string value, Spacing expected) + { + // Act + var result = System.Text.Json.JsonSerializer.Deserialize(value); + + // Assert + Assert.Equal(expected, result); + } + + public static IEnumerable DataForConvert_From_Json_IsCorrect + => new[] + { + new object[] + { + "{ \"Left\": \"1mm\", \"Right\": \"2mm\", \"Top\": \"3mm\", \"Bottom\": \"4mm\" }", + new Spacing( + left: new Value(1, Unit.Millimeters), + right: new Value(2, Unit.Millimeters), + top: new Value(3, Unit.Millimeters), + bottom: new Value(4, Unit.Millimeters) + ), + }, + new object[]{ "\"All: 5mm\"", new Spacing(new Value(5, Unit.Millimeters)) } + }; } diff --git a/tests/Synercoding.Primitives.Tests/UnitTests.cs b/tests/Synercoding.Primitives.Tests/UnitTests.cs new file mode 100644 index 0000000..b683780 --- /dev/null +++ b/tests/Synercoding.Primitives.Tests/UnitTests.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using Xunit; + +namespace Synercoding.Primitives.Tests; + +public class UnitTests +{ + [Theory] + [MemberData(nameof(DataFor_TryParse_ReturnsTrue_And_Unit))] + public void TryParse_ReturnsTrue_And_Unit(string unit, Unit expected) + { + // Act + Unit.TryParse(unit, out var result); + + // Assert + Assert.Equal(expected, result); + } + + public static IEnumerable DataFor_TryParse_ReturnsTrue_And_Unit() + => new[] + { + new object[]{ "mm", Unit.Millimeters }, + new object[]{ "millimeter", Unit.Millimeters }, + new object[]{ "millimeters", Unit.Millimeters }, + new object[]{ "cm", Unit.Centimeters }, + new object[]{ "centimeter", Unit.Centimeters }, + new object[]{ "centimeters", Unit.Centimeters }, + new object[]{ "in", Unit.Inches }, + new object[]{ "inch", Unit.Inches }, + new object[]{ "inches", Unit.Inches }, + new object[]{ "pts", Unit.Points }, + new object[]{ "points", Unit.Points }, + new object[]{ "dpi(150)", Unit.Pixels(150) }, + }; +} diff --git a/tests/Synercoding.Primitives.Tests/ValueTests.cs b/tests/Synercoding.Primitives.Tests/ValueTests.cs index b40127b..509c357 100644 --- a/tests/Synercoding.Primitives.Tests/ValueTests.cs +++ b/tests/Synercoding.Primitives.Tests/ValueTests.cs @@ -233,6 +233,77 @@ public void TryParse_With_IsCorrect(string input, Value value) Assert.Equal(value, result); } + [Theory] + [MemberData(nameof(DataForConvert_To_Json_IsCorrect))] + public void Convert_To_Json_IsCorrect(Value value, string expected) + { + // Act + var result = System.Text.Json.JsonSerializer.Serialize(value); + + // Assert + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(DataForConvert_From_Json_IsCorrect))] + public void Convert_From_Json_IsCorrect(string value, Value expected) + { + // Act + var result = System.Text.Json.JsonSerializer.Deserialize(value); + + // Assert + Assert.Equal(expected, result); + } + + public static IEnumerable DataForConvert_To_Json_IsCorrect + => new[] + { + new object[]{ new Value(1, Unit.Millimeters), "\"1 mm\"" }, + new object[]{ new Value(10, Unit.Millimeters), "\"10 mm\"" }, + new object[]{ new Value(-15, Unit.Millimeters), "\"-15 mm\"" }, + new object[]{ new Value(1, Unit.Points), "\"1 pts\"" }, + new object[]{ new Value(10, Unit.Points), "\"10 pts\"" }, + new object[]{ new Value(-15, Unit.Points), "\"-15 pts\"" }, + new object[]{ new Value(1, Unit.Inches), "\"1 in\"" }, + new object[]{ new Value(10, Unit.Inches), "\"10 in\"" }, + new object[]{ new Value(-15, Unit.Inches), "\"-15 in\"" }, + new object[]{ new Value(1, Unit.Centimeters), "\"1 cm\"" }, + new object[]{ new Value(10, Unit.Centimeters), "\"10 cm\"" }, + new object[]{ new Value(-15, Unit.Centimeters), "\"-15 cm\"" }, + }; + + public static IEnumerable DataForConvert_From_Json_IsCorrect + => new[] + { + new object[]{ "\"1 mm\"", new Value(1, Unit.Millimeters) }, + new object[]{ "\"10 mm\"", new Value(10, Unit.Millimeters) }, + new object[]{ "\"-15 mm\"", new Value(-15, Unit.Millimeters) }, + new object[]{ "\"1 pts\"", new Value(1, Unit.Points) }, + new object[]{ "\"10 pts\"", new Value(10, Unit.Points) }, + new object[]{ "\"-15 pts\"", new Value(-15, Unit.Points) }, + new object[]{ "\"1 in\"", new Value(1, Unit.Inches) }, + new object[]{ "\"10 in\"", new Value(10, Unit.Inches) }, + new object[]{ "\"-15 in\"", new Value(-15, Unit.Inches) }, + new object[]{ "\"1 cm\"", new Value(1, Unit.Centimeters) }, + new object[]{ "\"10 cm\"", new Value(10, Unit.Centimeters) }, + new object[]{ "\"-15 cm\"", new Value(-15, Unit.Centimeters) }, + + new object[]{ "\"1mm\"", new Value(1, Unit.Millimeters) }, + new object[]{ "\"10mm\"", new Value(10, Unit.Millimeters) }, + new object[]{ "\"-15mm\"", new Value(-15, Unit.Millimeters) }, + new object[]{ "\"1pts\"", new Value(1, Unit.Points) }, + new object[]{ "\"10pts\"", new Value(10, Unit.Points) }, + new object[]{ "\"-15pts\"", new Value(-15, Unit.Points) }, + new object[]{ "\"1in\"", new Value(1, Unit.Inches) }, + new object[]{ "\"10in\"", new Value(10, Unit.Inches) }, + new object[]{ "\"-15in\"", new Value(-15, Unit.Inches) }, + new object[]{ "\"1cm\"", new Value(1, Unit.Centimeters) }, + new object[]{ "\"10cm\"", new Value(10, Unit.Centimeters) }, + new object[]{ "\"-15cm\"", new Value(-15, Unit.Centimeters) }, + + new object[]{ "{ \"Raw\": 12.3, \"Unit\": \"mm\" }", new Value(12.3, Unit.Millimeters) }, + }; + public static IEnumerable DataForToString_TryParse_Same_Value => new[] {