From 64ac6b4525384bac80c79b0bf958670e082432f6 Mon Sep 17 00:00:00 2001 From: desjoerd Date: Tue, 7 Jan 2025 21:23:50 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20Dictionary=20Extensions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extensions/DictionaryExtensions.cs | 64 ++++ src/OptionalValues/PublicAPI.Unshipped.txt | 5 + .../Extensions/DictionaryExtensionsTest.cs | 295 ++++++++++++++++++ 3 files changed, 364 insertions(+) create mode 100644 src/OptionalValues/Extensions/DictionaryExtensions.cs create mode 100644 test/OptionalValues.Tests/Extensions/DictionaryExtensionsTest.cs diff --git a/src/OptionalValues/Extensions/DictionaryExtensions.cs b/src/OptionalValues/Extensions/DictionaryExtensions.cs new file mode 100644 index 0000000..2998852 --- /dev/null +++ b/src/OptionalValues/Extensions/DictionaryExtensions.cs @@ -0,0 +1,64 @@ +namespace OptionalValues.Extensions; + +/// +/// Extension methods for . +/// +public static class DictionaryExtensions +{ + /// + /// Gets the value associated with the specified key, returning if the key is not found. + /// + /// A specified when the key is found, or an when the key is not found. + public static OptionalValue GetOptionalValue(this IDictionary dictionary, TKey key) + { + ArgumentNullException.ThrowIfNull(dictionary); + ArgumentNullException.ThrowIfNull(key); + + return dictionary.TryGetValue(key, out T? value) ? new OptionalValue(value) : OptionalValue.Unspecified; + } + + /// + /// Adds the specified key and value to the dictionary if the value is specified. + /// + /// When value is specified and the key already exists in the dictionary. + /// The key is null. + public static void AddOptionalValue(this IDictionary dictionary, TKey key, OptionalValue value) + { + ArgumentNullException.ThrowIfNull(dictionary); + ArgumentNullException.ThrowIfNull(key); + + if (value.IsSpecified) + { + dictionary.Add(key, value.SpecifiedValue); + } + } + + /// + /// Adds the specified key and value to the dictionary if the value is specified. + /// + /// When value is specified and the key already exists in the dictionary. + /// The key is null. + /// if the value was added to the dictionary; otherwise when the already exists or the value is , . + public static bool TryAddOptionalValue(this IDictionary dictionary, TKey key, OptionalValue value) + { + ArgumentNullException.ThrowIfNull(dictionary); + ArgumentNullException.ThrowIfNull(key); + + return value.IsSpecified && dictionary.TryAdd(key, value.SpecifiedValue); + } + + /// + /// Sets the specified key and value in the dictionary if the value is specified. + /// + /// The key is null. + public static void SetOptionalValue(this IDictionary dictionary, TKey key, OptionalValue value) + { + ArgumentNullException.ThrowIfNull(dictionary); + ArgumentNullException.ThrowIfNull(key); + + if (value.IsSpecified) + { + dictionary[key] = value.SpecifiedValue; + } + } +} \ No newline at end of file diff --git a/src/OptionalValues/PublicAPI.Unshipped.txt b/src/OptionalValues/PublicAPI.Unshipped.txt index 6459d04..aa77a42 100644 --- a/src/OptionalValues/PublicAPI.Unshipped.txt +++ b/src/OptionalValues/PublicAPI.Unshipped.txt @@ -1,6 +1,11 @@ +OptionalValues.Extensions.DictionaryExtensions OptionalValues.OptionalValueJsonConverterAttribute OptionalValues.OptionalValueJsonConverterAttribute.InnerConverterType.get -> System.Type? OptionalValues.OptionalValueJsonConverterAttribute.OptionalValueJsonConverterAttribute() -> void OptionalValues.OptionalValueJsonConverterAttribute.OptionalValueJsonConverterAttribute(System.Type! innerConverterType) -> void override OptionalValues.OptionalValueJsonConverterAttribute.CreateConverter(System.Type! typeToConvert) -> System.Text.Json.Serialization.JsonConverter? +static OptionalValues.Extensions.DictionaryExtensions.AddOptionalValue(this System.Collections.Generic.IDictionary! dictionary, TKey key, OptionalValues.OptionalValue value) -> void +static OptionalValues.Extensions.DictionaryExtensions.GetOptionalValue(this System.Collections.Generic.IDictionary! dictionary, TKey key) -> OptionalValues.OptionalValue +static OptionalValues.Extensions.DictionaryExtensions.SetOptionalValue(this System.Collections.Generic.IDictionary! dictionary, TKey key, OptionalValues.OptionalValue value) -> void +static OptionalValues.Extensions.DictionaryExtensions.TryAddOptionalValue(this System.Collections.Generic.IDictionary! dictionary, TKey key, OptionalValues.OptionalValue value) -> bool virtual OptionalValues.OptionalValueJsonConverterAttribute.CreateInnerConverter() -> System.Text.Json.Serialization.JsonConverter! \ No newline at end of file diff --git a/test/OptionalValues.Tests/Extensions/DictionaryExtensionsTest.cs b/test/OptionalValues.Tests/Extensions/DictionaryExtensionsTest.cs new file mode 100644 index 0000000..1b2a314 --- /dev/null +++ b/test/OptionalValues.Tests/Extensions/DictionaryExtensionsTest.cs @@ -0,0 +1,295 @@ +using OptionalValues.Extensions; + +namespace OptionalValues.Tests.Extensions; + +public class DictionaryExtensionsTest +{ + public class GetOptionalValue : DictionaryExtensionsTest + { + [Fact] + public void ReturnsUnspecifiedWhenKeyIsNotFound() + { + // Arrange + var dictionary = new Dictionary(); + + // Act + OptionalValue result = dictionary.GetOptionalValue("key"); + + // Assert + Assert.False(result.IsSpecified); + } + + [Fact] + public void ReturnsSpecifiedValueWhenKeyIsFound() + { + // Arrange + var dictionary = new Dictionary + { + ["key"] = 42 + }; + + // Act + OptionalValue result = dictionary.GetOptionalValue("key"); + + // Assert + Assert.True(result.IsSpecified); + Assert.Equal(42, result.SpecifiedValue); + } + + [Fact] + public void ThrowsArgumentNullExceptionWhenDictionaryIsNull() + { + // Arrange + IDictionary dictionary = null!; + + // Act + Action action = () => dictionary.GetOptionalValue("key"); + + // Assert + ArgumentNullException exception = Assert.Throws(action); + Assert.Equal("dictionary", exception.ParamName); + } + + [Fact] + public void ThrowsArgumentNullExceptionWhenKeyIsNull() + { + // Arrange + var dictionary = new Dictionary(); + + // Act + Action action = () => dictionary.GetOptionalValue(null!); + + // Assert + ArgumentNullException exception = Assert.Throws(action); + Assert.Equal("key", exception.ParamName); + } + } + + public class AddOptionalValue : DictionaryExtensionsTest + { + [Fact] + public void AddsValueWhenSpecified() + { + // Arrange + var dictionary = new Dictionary(); + + // Act + dictionary.AddOptionalValue("key", new OptionalValue(42)); + + // Assert + Assert.Equal(42, dictionary["key"]); + } + + [Fact] + public void DoesNotAddValueWhenUnspecified() + { + // Arrange + var dictionary = new Dictionary(); + + // Act + dictionary.AddOptionalValue("key", OptionalValue.Unspecified); + + // Assert + Assert.Empty(dictionary); + } + + [Fact] + public void ThrowsArgumentNullExceptionWhenDictionaryIsNull() + { + // Arrange + IDictionary dictionary = null!; + + // Act + Action action = () => dictionary.AddOptionalValue("key", new OptionalValue(42)); + + // Assert + ArgumentNullException exception = Assert.Throws(action); + Assert.Equal("dictionary", exception.ParamName); + } + + [Fact] + public void ThrowsArgumentNullExceptionWhenKeyIsNull() + { + // Arrange + var dictionary = new Dictionary(); + + // Act + Action action = () => dictionary.AddOptionalValue(null!, new OptionalValue(42)); + + // Assert + ArgumentNullException exception = Assert.Throws(action); + Assert.Equal("key", exception.ParamName); + } + + [Fact] + public void ThrowsArgumentExceptionWhenKeyAlreadyExists() + { + // Arrange + var dictionary = new Dictionary + { + ["key"] = 42 + }; + + // Act + Action action = () => dictionary.AddOptionalValue("key", new OptionalValue(42)); + + // Assert + Assert.Throws(action); + } + } + + public class TryAddOptionalValue : DictionaryExtensionsTest + { + [Fact] + public void AddsValueWhenSpecified() + { + // Arrange + var dictionary = new Dictionary(); + + // Act + var result = dictionary.TryAddOptionalValue("key", new OptionalValue(42)); + + // Assert + Assert.True(result); + Assert.Equal(42, dictionary["key"]); + } + + [Fact] + public void DoesNotAddValueWhenUnspecified() + { + // Arrange + var dictionary = new Dictionary(); + + // Act + var result = dictionary.TryAddOptionalValue("key", OptionalValue.Unspecified); + + // Assert + Assert.False(result); + Assert.Empty(dictionary); + } + + [Fact] + public void DoesNotAddWhenKeyExists() + { + // Arrange + var dictionary = new Dictionary + { + ["key"] = 42 + }; + + // Act + var result = dictionary.TryAddOptionalValue("key", new OptionalValue(9000)); + + // Assert + Assert.False(result); + Assert.Equal(42, dictionary["key"]); + } + + [Fact] + public void ThrowsArgumentNullExceptionWhenDictionaryIsNull() + { + // Arrange + IDictionary dictionary = null!; + + // Act + Action action = () => dictionary.TryAddOptionalValue("key", new OptionalValue(42)); + + // Assert + ArgumentNullException exception = Assert.Throws(action); + Assert.Equal("dictionary", exception.ParamName); + } + + [Fact] + public void ThrowsArgumentNullExceptionWhenKeyIsNull() + { + // Arrange + var dictionary = new Dictionary(); + + // Act + Action action = () => dictionary.TryAddOptionalValue(null!, new OptionalValue(42)); + + // Assert + ArgumentNullException exception = Assert.Throws(action); + Assert.Equal("key", exception.ParamName); + } + } + + public class SetOptionalValue : DictionaryExtensionsTest + { + [Fact] + public void SetsValueWhenSpecified() + { + // Arrange + var dictionary = new Dictionary + { + ["key"] = 42 + }; + + // Act + dictionary.SetOptionalValue("key", new OptionalValue(9000)); + + // Assert + Assert.Equal(9000, dictionary["key"]); + } + + [Fact] + public void DoesNotSetValueWhenUnspecified() + { + // Arrange + var dictionary = new Dictionary + { + ["key"] = 42 + }; + + // Act + dictionary.SetOptionalValue("key", OptionalValue.Unspecified); + + // Assert + Assert.Equal(42, dictionary["key"]); + } + + [Fact] + public void OverwritesValueWhenSpecified() + { + // Arrange + var dictionary = new Dictionary + { + ["key"] = 42 + }; + + // Act + dictionary.SetOptionalValue("key", new OptionalValue(9000)); + + // Assert + Assert.Equal(9000, dictionary["key"]); + } + + [Fact] + public void ThrowsArgumentNullExceptionWhenDictionaryIsNull() + { + // Arrange + IDictionary dictionary = null!; + + // Act + Action action = () => dictionary.SetOptionalValue("key", new OptionalValue(42)); + + // Assert + ArgumentNullException exception = Assert.Throws(action); + Assert.Equal("dictionary", exception.ParamName); + } + + [Fact] + public void ThrowsArgumentNullExceptionWhenKeyIsNull() + { + // Arrange + var dictionary = new Dictionary(); + + // Act + Action action = () => dictionary.SetOptionalValue(null!, new OptionalValue(42)); + + // Assert + ArgumentNullException exception = Assert.Throws(action); + Assert.Equal("key", exception.ParamName); + } + } +} \ No newline at end of file