diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index d42292fdd..4b6060035 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -24,7 +24,6 @@ jobs: with: dotnet-version: | 6.0.x - 7.0.x 8.0.x - name: Add NuGet source @@ -39,9 +38,6 @@ jobs: - name: Test .NET 6 run: dotnet test --no-build --verbosity normal --configuration Release --framework net6.0 --collect:"XPlat Code Coverage" --results-directory test-results/net6.0 - - name: Test .NET 7 - run: dotnet test --no-build --verbosity normal --configuration Release --framework net7.0 --collect:"XPlat Code Coverage" --results-directory test-results/net7.0 - - name: Test .NET 8 run: dotnet test --no-build --verbosity normal --configuration Release --framework net8.0 --collect:"XPlat Code Coverage" --results-directory test-results/net8.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 00ce82343..759b0165d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - X10D: Added `TextWriter.WriteLineNoAlloc(uint[, ReadOnlySpan[, IFormatProvider]])`. - X10D: Added `TextWriter.WriteLineNoAlloc(long[, ReadOnlySpan[, IFormatProvider]])`. - X10D: Added `TextWriter.WriteLineNoAlloc(ulong[, ReadOnlySpan[, IFormatProvider]])`. +- X10D: Added `Range.GetEnumerator` (and `RangeEnumerator`), implementing Python-esque `for` loops in C#. - X10D: Added `string.ConcatIf`. - X10D: Added `string.MDBold`, `string.MDCode`, `string.MDCodeBlock([string])`, `string.MDHeading(int)`, `string.MDItalic`, `string.MDLink`, `string.MDStrikeOut`, and `string.MDUnderline` for Markdown formatting. @@ -39,7 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 BigEndian/LittleEndian methods. - X10D: `Stream.GetHash<>` and `Stream.TryWriteHash<>` now throw ArgumentException in lieu of TypeInitializationException. -- X10D: `char.IsEmoji` no longer allocates for .NET 7. +- X10D: `char.IsEmoji` no longer allocates for .NET 7+. - X10D: `string.Repeat` is now more efficient. ### Removed @@ -48,6 +49,7 @@ TypeInitializationException. - X10D: Removed `Endianness` enum. - X10D: Removed `Span.Replace(T, T)` for .NET 8 target. - X10D: Removed .NET Standard 2.1 target. +- X10D: Removed extensions for `Progress`. These are already provided by [`Observable.FromEventPattern`](https://learn.microsoft.com/en-us/previous-versions/dotnet/reactive-extensions/hh229424(v=vs.103)). - X10D.Hosting: Removed .NET Standard 2.1 target. - X10D.DSharpPlus: Complete sunset of library. This library will not be updated to support DSharpPlus v5.0.0 (#83). - X10D.Unity: Complete sunset of library. This library will not be updated effective immediately (#86). @@ -56,6 +58,7 @@ TypeInitializationException. ### Fixed +- X10D: Fixed `decimal.TryWriteBigEndianBytes` and `decimal.TryWriteLittleEndianBytes`. - X10D.Hosting: Fixed `AddHostedSingleton` not accepting an interface as the service type. ## [3.3.0] - 2023-08-21 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5aae0b44a..a6b5007c6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,7 +5,7 @@ or submit a pull request. ### Pull request guidelines -This project uses C# 11.0 language features where feasible, and adheres to StyleCop rules with some minor adjustments. +This project uses C# 12.0 language features where feasible, and adheres to StyleCop rules with some minor adjustments. There is an `.editorconfig` included in this repository. For quick and painless pull requests, ensure that the analyzer does not throw warnings. @@ -17,7 +17,7 @@ convetional commits specification. Below are a few pointers to which you may refer, but keep in mind this is not an exhaustive list: -- Use C# 11.0 features where possible +- Use C# 12.0 features where possible - Try to ensure code is CLS-compliant. Where this is not possible, decorate methods with `CLSCompliantAttribute` and pass `false` - Follow all .NET guidelines and coding conventions. See https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions diff --git a/Directory.Build.props b/Directory.Build.props index 9f2994d40..283ddde96 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 11.0 + 12.0 true true true diff --git a/X10D.Hosting/X10D.Hosting.csproj b/X10D.Hosting/X10D.Hosting.csproj index b54a51874..cc93b6385 100644 --- a/X10D.Hosting/X10D.Hosting.csproj +++ b/X10D.Hosting/X10D.Hosting.csproj @@ -1,7 +1,7 @@ - net8.0;net7.0;net6.0 + net8.0;net6.0 diff --git a/X10D.Tests/X10D.Tests.csproj b/X10D.Tests/X10D.Tests.csproj index eb3895cbb..9ac986d07 100644 --- a/X10D.Tests/X10D.Tests.csproj +++ b/X10D.Tests/X10D.Tests.csproj @@ -1,7 +1,7 @@ - net8.0;net7.0;net6.0 + net8.0;net6.0 false true xml,cobertura diff --git a/X10D.Tests/src/Core/RangeTests.cs b/X10D.Tests/src/Core/RangeTests.cs new file mode 100644 index 000000000..7acdbce9e --- /dev/null +++ b/X10D.Tests/src/Core/RangeTests.cs @@ -0,0 +1,66 @@ +using NUnit.Framework; +using X10D.Core; + +namespace X10D.Tests.Core; + +[TestFixture] +internal class RangeTests +{ + [Test] + public void Range_GetEnumerator_ShouldReturnRangeEnumerator() + { + Assert.Multiple(() => + { + Assert.That(5..10, Is.TypeOf()); + Assert.That((5..10).GetEnumerator(), Is.TypeOf()); + }); + } + + [Test] + public void Loop_OverRange0To10_ShouldCountFrom0To10Inclusive() + { + int value = 0; + + foreach (int i in 0..10) + { + Assert.That(i, Is.EqualTo(value)); + value++; + } + } + + [Test] + public void Loop_OverRangeNegative5To5_ShouldCountFromNegative5To5Inclusive() + { + int value = -5; + + foreach (int i in ^5..5) + { + Assert.That(i, Is.EqualTo(value)); + value++; + } + } + + [Test] + public void Loop_OverRange5ToNegative5_ShouldCountFrom5ToNegative5Inclusive() + { + int value = 5; + + foreach (int i in 5..^5) + { + Assert.That(i, Is.EqualTo(value)); + value--; + } + } + + [Test] + public void Loop_OverRange10To0_ShouldCountFrom10To0Inclusive() + { + int value = 10; + + foreach (int i in 10..0) + { + Assert.That(i, Is.EqualTo(value)); + value--; + } + } +} diff --git a/X10D.Tests/src/IO/DecimalTests.cs b/X10D.Tests/src/IO/DecimalTests.cs new file mode 100644 index 000000000..46e70776c --- /dev/null +++ b/X10D.Tests/src/IO/DecimalTests.cs @@ -0,0 +1,72 @@ +using NUnit.Framework; +using X10D.IO; + +namespace X10D.Tests.IO; + +[TestFixture] +internal class DecimalTests +{ + [Test] + public void GetBigEndianBytes_ShouldReturnBytes_InBigEndian() + { + const decimal value = 1234m; + byte[] expected = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 210]; + + byte[] bytes = value.GetBigEndianBytes(); + + CollectionAssert.AreEqual(expected, bytes); + } + + [Test] + public void GetLittleEndianBytes_ShouldReturnBytes_InLittleEndian() + { + const decimal value = 1234m; + byte[] expected = [210, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + byte[] bytes = value.GetLittleEndianBytes(); + + CollectionAssert.AreEqual(expected, bytes); + } + + [Test] + public void TryWriteBigEndianBytes_ShouldWriteBytes_InBigEndian() + { + const decimal value = 1234m; + byte[] expected = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 210]; + + Span bytes = stackalloc byte[16]; + Assert.That(value.TryWriteBigEndianBytes(bytes)); + + CollectionAssert.AreEqual(expected, bytes.ToArray()); + } + + [Test] + public void TryWriteLittleEndianBytes_ShouldWriteBytes_InLittleEndian() + { + const decimal value = 1234m; + byte[] expected = [210, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + Span bytes = stackalloc byte[16]; + Assert.That(value.TryWriteLittleEndianBytes(bytes)); + + CollectionAssert.AreEqual(expected, bytes.ToArray()); + } + + [Test] + public void TryWriteBigEndianBytes_ShouldReturnFalse_GivenSmallSpan() + { + const decimal value = 1234m; + + Span bytes = Span.Empty; + Assert.That(value.TryWriteBigEndianBytes(bytes), Is.False); + } + + [Test] + public void TryWriteLittleEndianBytes_ShouldReturnFalse_GivenSmallSpan() + { + const decimal value = 1234m; + + Span bytes = Span.Empty; + Assert.That(value.TryWriteLittleEndianBytes(bytes), Is.False); + } +} diff --git a/X10D.Tests/src/IO/StreamTests.WriteDecimal.cs b/X10D.Tests/src/IO/StreamTests.WriteDecimal.cs index 911dd5d64..15e753847 100644 --- a/X10D.Tests/src/IO/StreamTests.WriteDecimal.cs +++ b/X10D.Tests/src/IO/StreamTests.WriteDecimal.cs @@ -48,9 +48,10 @@ public void WriteBigEndian_ShouldWriteBigEndian_GivenDecimalArgument() Span actual = stackalloc byte[16]; ReadOnlySpan expected = stackalloc byte[] { - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x10, 0x00, 0x00 + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x68 }; int read = stream.Read(actual); + Trace.WriteLine(string.Join(' ', actual.ToArray())); Assert.That(read, Is.EqualTo(16)); CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray()); diff --git a/X10D.Tests/src/Reactive/ProgressTests.cs b/X10D.Tests/src/Reactive/ProgressTests.cs deleted file mode 100644 index 5b8f18057..000000000 --- a/X10D.Tests/src/Reactive/ProgressTests.cs +++ /dev/null @@ -1,67 +0,0 @@ -using NUnit.Framework; -using X10D.Reactive; - -namespace X10D.Tests.Reactive; - -[TestFixture] -internal class ProgressTests -{ - [Test] - public void OnProgressChanged_ShouldCallCompletionDelegate_GivenCompletionValue() - { - var subscriberWasCalled = false; - var completionWasCalled = false; - - var progress = new Progress(); - progress.OnProgressChanged(1.0f).Subscribe(_ => subscriberWasCalled = true, () => completionWasCalled = true); - - ((IProgress)progress).Report(0.5f); - ((IProgress)progress).Report(1.0f); - - Thread.Sleep(1000); - Assert.That(subscriberWasCalled); - Assert.That(completionWasCalled); - } - - [Test] - public void OnProgressChanged_ShouldCallSubscribers_OnProgressChanged() - { - var subscriberWasCalled = false; - - var progress = new Progress(); - progress.OnProgressChanged().Subscribe(_ => subscriberWasCalled = true); - - ((IProgress)progress).Report(0.5f); - - Thread.Sleep(1000); - Assert.That(subscriberWasCalled); - } - - [Test] - public void OnProgressChanged_ShouldCallSubscribers_OnProgressChanged_GivenCompletionValue() - { - var subscriberWasCalled = false; - - var progress = new Progress(); - progress.OnProgressChanged(1.0f).Subscribe(_ => subscriberWasCalled = true); - - ((IProgress)progress).Report(0.5f); - - Thread.Sleep(1000); - Assert.That(subscriberWasCalled); - } - - [Test] - public void OnProgressChanged_ShouldThrowArgumentNullException_GivenNullProgress() - { - Progress progress = null!; - Assert.Throws(() => progress.OnProgressChanged()); - } - - [Test] - public void OnProgressChanged_ShouldThrowArgumentNullException_GivenNullProgressAndCompletionValue() - { - Progress progress = null!; - Assert.Throws(() => progress.OnProgressChanged(1.0f)); - } -} diff --git a/X10D/X10D.csproj b/X10D/X10D.csproj index 8f36b52a5..592e77308 100644 --- a/X10D/X10D.csproj +++ b/X10D/X10D.csproj @@ -1,7 +1,7 @@ - net8.0;net7.0;net6.0 + net8.0;net6.0 diff --git a/X10D/src/Core/RangeEnumerator.cs b/X10D/src/Core/RangeEnumerator.cs new file mode 100644 index 000000000..e2dd59b26 --- /dev/null +++ b/X10D/src/Core/RangeEnumerator.cs @@ -0,0 +1,53 @@ +namespace X10D.Core; + +/// +/// Enumerates the indices of a . +/// +public struct RangeEnumerator +{ + private readonly bool _decrement; + private readonly int _endValue; + + /// + /// Initializes a new instance of the structure. + /// + /// The range over which to enumerate. + public RangeEnumerator(Range range) + { + Index start = range.Start; + Index end = range.End; + + int startValue = start.IsFromEnd ? -start.Value : start.Value; + _endValue = end.IsFromEnd ? -end.Value : end.Value; + + _decrement = _endValue < startValue; + Current = _decrement ? startValue + 1 : startValue - 1; + } + + /// + /// Gets the element in the collection at the current position of the enumerator. + /// + /// The element in the collection at the current position of the enumerator. + public int Current { get; private set; } + + /// + /// Advances the enumerator to the next element of the collection. + /// + /// + /// if the enumerator was successfully advanced to the next element; if + /// the enumerator has passed the end of the collection. + /// + public bool MoveNext() + { + int value = Current; + + if (_decrement) + { + Current--; + return value > _endValue; + } + + Current++; + return value < _endValue; + } +} diff --git a/X10D/src/Core/RangeExtensions.cs b/X10D/src/Core/RangeExtensions.cs new file mode 100644 index 000000000..6c9192277 --- /dev/null +++ b/X10D/src/Core/RangeExtensions.cs @@ -0,0 +1,48 @@ +namespace X10D.Core; + +/// +/// Extension methods for . +/// +public static class RangeExtensions +{ + /// + /// Allows the ability to use a for loop to iterate over the indices of a . The indices of the + /// range are the inclusive lower and upper bounds of the enumeration. + /// + /// The range whose indices over which will be enumerated. + /// A that will enumerate over the indices of . + /// + /// This method aims to implement Python-esque for loops in C# by taking advantage of the language syntax used to define + /// a value. Negative bounds may be specified using the C# ^ operator, or by providing an + /// whose property is . + /// + /// + /// The following example counts from 0 to 10 inclusive: + /// + /// foreach (var i in 0..10) + /// { + /// Console.WriteLine(i); + /// } + /// + /// + /// To use negative bounds, use the ^ operator. The following example counts from -5 to 5 inclusive: + /// + /// foreach (var i in ^5..5) + /// { + /// Console.WriteLine(i); + /// } + /// + /// + /// Decrementing enumeration is supported. The following example counts from 5 to -5 inclusive: + /// + /// foreach (var i in 5..^5) + /// { + /// Console.WriteLine(i); + /// } + /// + /// + public static RangeEnumerator GetEnumerator(this Range range) + { + return new RangeEnumerator(range); + } +} diff --git a/X10D/src/Core/SpanExtensions.cs b/X10D/src/Core/SpanExtensions.cs index c1f2a1e9f..ab79a160e 100644 --- a/X10D/src/Core/SpanExtensions.cs +++ b/X10D/src/Core/SpanExtensions.cs @@ -92,14 +92,12 @@ public static bool Contains(this ReadOnlySpan span, T value) where T : str } // dotcover disable - //NOSONAR default: #if NET7_0_OR_GREATER throw new UnreachableException(string.Format(ExceptionMessages.EnumSizeIsUnexpected, Unsafe.SizeOf())); #else throw new ArgumentException(string.Format(ExceptionMessages.EnumSizeIsUnexpected, Unsafe.SizeOf())); #endif - //NOSONAR // dotcover enable } } diff --git a/X10D/src/IO/DecimalExtensions.cs b/X10D/src/IO/DecimalExtensions.cs index 68b9a6e51..e01f5910c 100644 --- a/X10D/src/IO/DecimalExtensions.cs +++ b/X10D/src/IO/DecimalExtensions.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Diagnostics.Contracts; using System.Runtime.InteropServices; @@ -13,11 +12,11 @@ public static class DecimalExtensions /// Converts the current decimal number into an array of bytes, as little endian. /// /// The value. - /// An array of bytes with length 4. + /// An array of bytes with length 16. [Pure] public static byte[] GetBigEndianBytes(this decimal value) { - Span buffer = stackalloc byte[4]; + Span buffer = stackalloc byte[16]; value.TryWriteBigEndianBytes(buffer); return buffer.ToArray(); } @@ -26,11 +25,11 @@ public static byte[] GetBigEndianBytes(this decimal value) /// Converts the current decimal number into an array of bytes, as little endian. /// /// The value. - /// An array of bytes with length 4. + /// An array of bytes with length 16. [Pure] public static byte[] GetLittleEndianBytes(this decimal value) { - Span buffer = stackalloc byte[4]; + Span buffer = stackalloc byte[16]; value.TryWriteLittleEndianBytes(buffer); return buffer.ToArray(); } @@ -44,23 +43,17 @@ public static byte[] GetLittleEndianBytes(this decimal value) public static bool TryWriteBigEndianBytes(this decimal value, Span destination) { Span buffer = stackalloc int[4]; - GetBits(value, buffer); + decimal.GetBits(value, buffer); - if (buffer[0].TryWriteBigEndianBytes(destination[..4]) && - buffer[1].TryWriteBigEndianBytes(destination[4..8]) && - buffer[2].TryWriteBigEndianBytes(destination[8..12]) && - buffer[3].TryWriteBigEndianBytes(destination[12..])) - { - if (BitConverter.IsLittleEndian) - { - destination.Reverse(); - } + Span result = stackalloc byte[16]; + MemoryMarshal.Cast(buffer).CopyTo(result); - return true; + if (BitConverter.IsLittleEndian) + { + result.Reverse(); } - destination.Clear(); - return false; + return result.TryCopyTo(destination); } /// @@ -72,44 +65,16 @@ public static bool TryWriteBigEndianBytes(this decimal value, Span destina public static bool TryWriteLittleEndianBytes(this decimal value, Span destination) { Span buffer = stackalloc int[4]; - GetBits(value, buffer); + decimal.GetBits(value, buffer); - if (buffer[0].TryWriteLittleEndianBytes(destination[..4]) && - buffer[1].TryWriteLittleEndianBytes(destination[4..8]) && - buffer[2].TryWriteLittleEndianBytes(destination[8..12]) && - buffer[3].TryWriteLittleEndianBytes(destination[12..])) - { - if (!BitConverter.IsLittleEndian) - { - destination.Reverse(); - } + Span result = stackalloc byte[16]; + MemoryMarshal.Cast(buffer).CopyTo(result); - return true; + if (!BitConverter.IsLittleEndian) + { + result.Reverse(); } - destination.Clear(); - return false; - } - - private static void GetBits(decimal value, Span destination) - { - _ = decimal.GetBits(value, destination); - } - -#if !NET5_0_OR_GREATER - private static void WriteBits(Span destination, Span buffer) - { - var flags = MemoryMarshal.Read(buffer[..4]); - var hi = MemoryMarshal.Read(buffer[4..8]); - var lo = MemoryMarshal.Read(buffer[8..]); - - var low = (uint)lo; - var mid = (uint)(lo >> 32); - - destination[0] = (int)low; - destination[1] = (int)mid; - destination[2] = hi; - destination[3] = flags; + return result.TryCopyTo(destination); } -#endif } diff --git a/X10D/src/Reactive/ObservableDisposer.cs b/X10D/src/Reactive/ObservableDisposer.cs deleted file mode 100644 index b9351122d..000000000 --- a/X10D/src/Reactive/ObservableDisposer.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace X10D.Reactive; - -/// -/// Represents a disposable that removes an observer from a collection of observers. -/// -internal readonly struct ObservableDisposer : IDisposable -{ - private readonly HashSet> _observers; - private readonly IObserver _observer; - private readonly Action? _additionalAction; - - /// - /// Initializes a new instance of the struct. - /// - /// A collection of observers from which to remove the specified observer. - /// The observer to remove from the collection. - /// The additional action to run on dispose. - public ObservableDisposer(HashSet> observers, IObserver observer, Action? additionalAction) - { - _observers = observers ?? throw new ArgumentNullException(nameof(observers)); - _observer = observer ?? throw new ArgumentNullException(nameof(observer)); - _additionalAction = additionalAction; - } - - /// - /// Removes the observer from the collection of observers. - /// - public void Dispose() - { - _observers.Remove(_observer); - _additionalAction?.Invoke(); - } -} diff --git a/X10D/src/Reactive/ProgressExtensions.cs b/X10D/src/Reactive/ProgressExtensions.cs deleted file mode 100644 index 93f696e98..000000000 --- a/X10D/src/Reactive/ProgressExtensions.cs +++ /dev/null @@ -1,89 +0,0 @@ -namespace X10D.Reactive; - -/// -/// Provides extension methods for . -/// -public static class ProgressExtensions -{ - /// - /// Wraps the event of the current in an - /// object. - /// - /// The progress whose event to wrap. - /// The type of progress update value. - /// - /// An object that wraps the event of the current - /// . - /// - /// is . - public static IObservable OnProgressChanged(this Progress progress) - { - if (progress is null) - { - throw new ArgumentNullException(nameof(progress)); - } - - var progressObservable = new ProgressObservable(); - - void ProgressChangedHandler(object? sender, T args) - { - IObserver[] observers = progressObservable.Observers; - - for (var index = 0; index < observers.Length; index++) - { - observers[index].OnNext(args); - } - } - - progress.ProgressChanged += ProgressChangedHandler; - progressObservable.OnDispose = () => progress.ProgressChanged -= ProgressChangedHandler; - - return progressObservable; - } - - /// - /// Wraps the event of the current in an - /// object, and completes the observable when the progress reaches the specified value. - /// - /// The progress whose event to wrap. - /// The value that indicates completion. - /// The type of progress update value. - /// - /// An object that wraps the event of the current - /// . - /// - /// is . - public static IObservable OnProgressChanged(this Progress progress, T completeValue) - { - if (progress is null) - { - throw new ArgumentNullException(nameof(progress)); - } - - var progressObservable = new ProgressObservable(); - var comparer = EqualityComparer.Default; - - void ProgressChangedHandler(object? sender, T args) - { - IObserver[] observers = progressObservable.Observers; - - for (var index = 0; index < observers.Length; index++) - { - observers[index].OnNext(args); - } - - if (comparer.Equals(args, completeValue)) - { - for (var index = 0; index < observers.Length; index++) - { - observers[index].OnCompleted(); - } - } - } - - progress.ProgressChanged += ProgressChangedHandler; - progressObservable.OnDispose = () => progress.ProgressChanged -= ProgressChangedHandler; - - return progressObservable; - } -} diff --git a/X10D/src/Reactive/ProgressObservable.cs b/X10D/src/Reactive/ProgressObservable.cs deleted file mode 100644 index 32a08707a..000000000 --- a/X10D/src/Reactive/ProgressObservable.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace X10D.Reactive; - -/// -/// Represents a concrete implementation of that tracks progress of a . -/// -internal sealed class ProgressObservable : IObservable -{ - private readonly HashSet> _observers = new(); - - /// - /// Gets the observers. - /// - /// The observers. - public IObserver[] Observers - { - get => _observers.ToArray(); - } - - internal Action? OnDispose { get; set; } - - /// - /// Subscribes the specified observer to the progress tracker. - /// - /// The observer. - /// An object which can be disposed to unsubscribe from progress tracking. - public IDisposable Subscribe(IObserver observer) - { - if (observer is null) - { - throw new ArgumentNullException(nameof(observer)); - } - - _observers.Add(observer); - return new ObservableDisposer(_observers, observer, OnDispose); - } -} diff --git a/X10D/src/Text/RuneExtensions.cs b/X10D/src/Text/RuneExtensions.cs index 47349b1ac..c1f3ef942 100644 --- a/X10D/src/Text/RuneExtensions.cs +++ b/X10D/src/Text/RuneExtensions.cs @@ -98,7 +98,6 @@ public static string Repeat(this Rune value, int count) } // dotcover disable - //NOSONAR default: string exceptionFormat = ExceptionMessages.UnexpectedRuneUtf8SequenceLength; string message = string.Format(CultureInfo.CurrentCulture, exceptionFormat, length); @@ -107,7 +106,6 @@ public static string Repeat(this Rune value, int count) #else throw new InvalidOperationException(message); #endif - //NOSONAR // dotcover enable } } diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 000000000..f9f76f137 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,2 @@ +ignore: + - "X10D/src/ExceptionMessages.Designer.cs" diff --git a/docfx_project/articles/migration-from-3.x.x.md b/docfx_project/articles/migration-from-3.x.x.md index 1dbe1796b..da8807317 100644 --- a/docfx_project/articles/migration-from-3.x.x.md +++ b/docfx_project/articles/migration-from-3.x.x.md @@ -3,6 +3,9 @@ X10D 4.0.0 is a major release that introduces breaking changes. This document will help you migrate your code from 3.x.x to 4.0.0. +When a breaking change is mentioned, the compatibility mirrors that of the Microsoft documentation for .NET, which you +can find [here](https://learn.microsoft.com/en-us/dotnet/core/compatibility/categories). + ## Removed APIs ### X10D.DSharpPlus library @@ -12,8 +15,18 @@ wrapper library. However, I have since moved to using a different library, and a scope of X10D or in my best interest to maintain it. The library will remain available on NuGet until DSharpPlus release 5.0.0 as stable, and X10D.DSharpPlus will NOT be part of X10D 4.0.0. I'm sorry for any inconvenience this may cause. +### X10D.Unity library + +The `X10D.Unity` library has been removed. This library was used to provide extension methods for the Unity API. Due to +game development politics, I no longer feel it in my best interest to continue development of the Unity package. The +library will remain on NuGet for the foreseeable future but will no longer be maintained. The `upm` branch of the Git +repository will remain available indefinitely also. + + ### `Endianness` enum +**Source incompatible change** + The `Endianness` enum was used to specify the endianness of data when reading or writing to a stream. This was causing some clutter, and makes it harder to develop X10D, so it was removed. In its stead, any method which accepted an `Endianness` parameter now has two overloads: one for big-endian, and one for little-endian. For example, the following @@ -41,12 +54,16 @@ Span buffer = stackalloc byte[4]; ### `IEnumerable.ConcatOne(T)` extension method +**Source incompatible change** + The `IEnumerable.ConcatOne` extension method was used to concatenate a single item to an enumerable. At the time, I was unaware of the `Enumerable.Append` method, which does the same thing. As such, `ConcatOne` has been removed. There is no migration path for this, as the built in `Append` method from LINQ is a drop-in replacement. ## Exception Changes +**Source incompatible change** + If you were previously catching TypeInitializationException when calling `Stream.GetHash<>` or `Stream.TryWriteHash<>`, you will now need to catch a ArgumentException instead. The justification for this change is that ArgumentException is more general, and more easily understood by developers. \ No newline at end of file diff --git a/tools/Benchmarks/Benchmarks.csproj b/tools/Benchmarks/Benchmarks.csproj index 0791ad975..197c37b7e 100644 --- a/tools/Benchmarks/Benchmarks.csproj +++ b/tools/Benchmarks/Benchmarks.csproj @@ -1,7 +1,7 @@ - net8.0;net7.0;net6.0 + net8.0;net6.0 Release Exe true