-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
364 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
namespace OptionalValues.Extensions; | ||
|
||
/// <summary> | ||
/// Extension methods for <see cref="IDictionary{TKey, TValue}"/>. | ||
/// </summary> | ||
public static class DictionaryExtensions | ||
{ | ||
/// <summary> | ||
/// Gets the value associated with the specified key, returning <see cref="OptionalValue{T}.Unspecified"/> if the key is not found. | ||
/// </summary> | ||
/// <returns>A specified <see cref="OptionalValue{T}"/> when the key is found, or an <see cref="OptionalValue{T}.Unspecified"/> when the key is not found.</returns> | ||
public static OptionalValue<T> GetOptionalValue<TKey, T>(this IDictionary<TKey, T> dictionary, TKey key) | ||
{ | ||
ArgumentNullException.ThrowIfNull(dictionary); | ||
ArgumentNullException.ThrowIfNull(key); | ||
|
||
return dictionary.TryGetValue(key, out T? value) ? new OptionalValue<T>(value) : OptionalValue<T>.Unspecified; | ||
} | ||
|
||
/// <summary> | ||
/// Adds the specified key and value to the dictionary if the value is specified. | ||
/// </summary> | ||
/// <exception cref="ArgumentException">When value is specified and the key already exists in the dictionary.</exception> | ||
/// <exception cref="ArgumentNullException">The key is null.</exception> | ||
public static void AddOptionalValue<TKey, T>(this IDictionary<TKey, T> dictionary, TKey key, OptionalValue<T> value) | ||
{ | ||
ArgumentNullException.ThrowIfNull(dictionary); | ||
ArgumentNullException.ThrowIfNull(key); | ||
|
||
if (value.IsSpecified) | ||
{ | ||
dictionary.Add(key, value.SpecifiedValue); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Adds the specified key and value to the dictionary if the value is specified. | ||
/// </summary> | ||
/// <exception cref="ArgumentException">When value is specified and the key already exists in the dictionary.</exception> | ||
/// <exception cref="ArgumentNullException">The key is null.</exception> | ||
/// <returns><see langword="true"/> if the value was added to the dictionary; otherwise when the <paramref name="key"/> already exists or the value is <see cref="OptionalValue{T}.Unspecified"/>, <see langword="false"/>.</returns> | ||
public static bool TryAddOptionalValue<TKey, T>(this IDictionary<TKey, T> dictionary, TKey key, OptionalValue<T> value) | ||
{ | ||
ArgumentNullException.ThrowIfNull(dictionary); | ||
ArgumentNullException.ThrowIfNull(key); | ||
|
||
return value.IsSpecified && dictionary.TryAdd(key, value.SpecifiedValue); | ||
} | ||
|
||
/// <summary> | ||
/// Sets the specified key and value in the dictionary if the value is specified. | ||
/// </summary> | ||
/// <exception cref="ArgumentNullException">The key is null.</exception> | ||
public static void SetOptionalValue<TKey, T>(this IDictionary<TKey, T> dictionary, TKey key, OptionalValue<T> value) | ||
{ | ||
ArgumentNullException.ThrowIfNull(dictionary); | ||
ArgumentNullException.ThrowIfNull(key); | ||
|
||
if (value.IsSpecified) | ||
{ | ||
dictionary[key] = value.SpecifiedValue; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<TKey, T>(this System.Collections.Generic.IDictionary<TKey, T>! dictionary, TKey key, OptionalValues.OptionalValue<T> value) -> void | ||
static OptionalValues.Extensions.DictionaryExtensions.GetOptionalValue<TKey, T>(this System.Collections.Generic.IDictionary<TKey, T>! dictionary, TKey key) -> OptionalValues.OptionalValue<T> | ||
static OptionalValues.Extensions.DictionaryExtensions.SetOptionalValue<TKey, T>(this System.Collections.Generic.IDictionary<TKey, T>! dictionary, TKey key, OptionalValues.OptionalValue<T> value) -> void | ||
static OptionalValues.Extensions.DictionaryExtensions.TryAddOptionalValue<TKey, T>(this System.Collections.Generic.IDictionary<TKey, T>! dictionary, TKey key, OptionalValues.OptionalValue<T> value) -> bool | ||
virtual OptionalValues.OptionalValueJsonConverterAttribute.CreateInnerConverter() -> System.Text.Json.Serialization.JsonConverter! |
295 changes: 295 additions & 0 deletions
295
test/OptionalValues.Tests/Extensions/DictionaryExtensionsTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<string, int>(); | ||
|
||
// Act | ||
OptionalValue<int> result = dictionary.GetOptionalValue("key"); | ||
|
||
// Assert | ||
Assert.False(result.IsSpecified); | ||
} | ||
|
||
[Fact] | ||
public void ReturnsSpecifiedValueWhenKeyIsFound() | ||
{ | ||
// Arrange | ||
var dictionary = new Dictionary<string, int> | ||
{ | ||
["key"] = 42 | ||
}; | ||
|
||
// Act | ||
OptionalValue<int> result = dictionary.GetOptionalValue("key"); | ||
|
||
// Assert | ||
Assert.True(result.IsSpecified); | ||
Assert.Equal(42, result.SpecifiedValue); | ||
} | ||
|
||
[Fact] | ||
public void ThrowsArgumentNullExceptionWhenDictionaryIsNull() | ||
{ | ||
// Arrange | ||
IDictionary<string, int> dictionary = null!; | ||
|
||
// Act | ||
Action action = () => dictionary.GetOptionalValue("key"); | ||
|
||
// Assert | ||
ArgumentNullException exception = Assert.Throws<ArgumentNullException>(action); | ||
Assert.Equal("dictionary", exception.ParamName); | ||
} | ||
|
||
[Fact] | ||
public void ThrowsArgumentNullExceptionWhenKeyIsNull() | ||
{ | ||
// Arrange | ||
var dictionary = new Dictionary<string, int>(); | ||
|
||
// Act | ||
Action action = () => dictionary.GetOptionalValue(null!); | ||
|
||
// Assert | ||
ArgumentNullException exception = Assert.Throws<ArgumentNullException>(action); | ||
Assert.Equal("key", exception.ParamName); | ||
} | ||
} | ||
|
||
public class AddOptionalValue : DictionaryExtensionsTest | ||
{ | ||
[Fact] | ||
public void AddsValueWhenSpecified() | ||
{ | ||
// Arrange | ||
var dictionary = new Dictionary<string, int>(); | ||
|
||
// Act | ||
dictionary.AddOptionalValue("key", new OptionalValue<int>(42)); | ||
|
||
// Assert | ||
Assert.Equal(42, dictionary["key"]); | ||
} | ||
|
||
[Fact] | ||
public void DoesNotAddValueWhenUnspecified() | ||
{ | ||
// Arrange | ||
var dictionary = new Dictionary<string, int>(); | ||
|
||
// Act | ||
dictionary.AddOptionalValue("key", OptionalValue<int>.Unspecified); | ||
|
||
// Assert | ||
Assert.Empty(dictionary); | ||
} | ||
|
||
[Fact] | ||
public void ThrowsArgumentNullExceptionWhenDictionaryIsNull() | ||
{ | ||
// Arrange | ||
IDictionary<string, int> dictionary = null!; | ||
|
||
// Act | ||
Action action = () => dictionary.AddOptionalValue("key", new OptionalValue<int>(42)); | ||
|
||
// Assert | ||
ArgumentNullException exception = Assert.Throws<ArgumentNullException>(action); | ||
Assert.Equal("dictionary", exception.ParamName); | ||
} | ||
|
||
[Fact] | ||
public void ThrowsArgumentNullExceptionWhenKeyIsNull() | ||
{ | ||
// Arrange | ||
var dictionary = new Dictionary<string, int>(); | ||
|
||
// Act | ||
Action action = () => dictionary.AddOptionalValue(null!, new OptionalValue<int>(42)); | ||
|
||
// Assert | ||
ArgumentNullException exception = Assert.Throws<ArgumentNullException>(action); | ||
Assert.Equal("key", exception.ParamName); | ||
} | ||
|
||
[Fact] | ||
public void ThrowsArgumentExceptionWhenKeyAlreadyExists() | ||
{ | ||
// Arrange | ||
var dictionary = new Dictionary<string, int> | ||
{ | ||
["key"] = 42 | ||
}; | ||
|
||
// Act | ||
Action action = () => dictionary.AddOptionalValue("key", new OptionalValue<int>(42)); | ||
|
||
// Assert | ||
Assert.Throws<ArgumentException>(action); | ||
} | ||
} | ||
|
||
public class TryAddOptionalValue : DictionaryExtensionsTest | ||
{ | ||
[Fact] | ||
public void AddsValueWhenSpecified() | ||
{ | ||
// Arrange | ||
var dictionary = new Dictionary<string, int>(); | ||
|
||
// Act | ||
var result = dictionary.TryAddOptionalValue("key", new OptionalValue<int>(42)); | ||
|
||
// Assert | ||
Assert.True(result); | ||
Assert.Equal(42, dictionary["key"]); | ||
} | ||
|
||
[Fact] | ||
public void DoesNotAddValueWhenUnspecified() | ||
{ | ||
// Arrange | ||
var dictionary = new Dictionary<string, int>(); | ||
|
||
// Act | ||
var result = dictionary.TryAddOptionalValue("key", OptionalValue<int>.Unspecified); | ||
|
||
// Assert | ||
Assert.False(result); | ||
Assert.Empty(dictionary); | ||
} | ||
|
||
[Fact] | ||
public void DoesNotAddWhenKeyExists() | ||
{ | ||
// Arrange | ||
var dictionary = new Dictionary<string, int> | ||
{ | ||
["key"] = 42 | ||
}; | ||
|
||
// Act | ||
var result = dictionary.TryAddOptionalValue("key", new OptionalValue<int>(9000)); | ||
|
||
// Assert | ||
Assert.False(result); | ||
Assert.Equal(42, dictionary["key"]); | ||
} | ||
|
||
[Fact] | ||
public void ThrowsArgumentNullExceptionWhenDictionaryIsNull() | ||
{ | ||
// Arrange | ||
IDictionary<string, int> dictionary = null!; | ||
|
||
// Act | ||
Action action = () => dictionary.TryAddOptionalValue("key", new OptionalValue<int>(42)); | ||
|
||
// Assert | ||
ArgumentNullException exception = Assert.Throws<ArgumentNullException>(action); | ||
Assert.Equal("dictionary", exception.ParamName); | ||
} | ||
|
||
[Fact] | ||
public void ThrowsArgumentNullExceptionWhenKeyIsNull() | ||
{ | ||
// Arrange | ||
var dictionary = new Dictionary<string, int>(); | ||
|
||
// Act | ||
Action action = () => dictionary.TryAddOptionalValue(null!, new OptionalValue<int>(42)); | ||
|
||
// Assert | ||
ArgumentNullException exception = Assert.Throws<ArgumentNullException>(action); | ||
Assert.Equal("key", exception.ParamName); | ||
} | ||
} | ||
|
||
public class SetOptionalValue : DictionaryExtensionsTest | ||
{ | ||
[Fact] | ||
public void SetsValueWhenSpecified() | ||
{ | ||
// Arrange | ||
var dictionary = new Dictionary<string, int> | ||
{ | ||
["key"] = 42 | ||
}; | ||
|
||
// Act | ||
dictionary.SetOptionalValue("key", new OptionalValue<int>(9000)); | ||
|
||
// Assert | ||
Assert.Equal(9000, dictionary["key"]); | ||
} | ||
|
||
[Fact] | ||
public void DoesNotSetValueWhenUnspecified() | ||
{ | ||
// Arrange | ||
var dictionary = new Dictionary<string, int> | ||
{ | ||
["key"] = 42 | ||
}; | ||
|
||
// Act | ||
dictionary.SetOptionalValue("key", OptionalValue<int>.Unspecified); | ||
|
||
// Assert | ||
Assert.Equal(42, dictionary["key"]); | ||
} | ||
|
||
[Fact] | ||
public void OverwritesValueWhenSpecified() | ||
{ | ||
// Arrange | ||
var dictionary = new Dictionary<string, int> | ||
{ | ||
["key"] = 42 | ||
}; | ||
|
||
// Act | ||
dictionary.SetOptionalValue("key", new OptionalValue<int>(9000)); | ||
|
||
// Assert | ||
Assert.Equal(9000, dictionary["key"]); | ||
} | ||
|
||
[Fact] | ||
public void ThrowsArgumentNullExceptionWhenDictionaryIsNull() | ||
{ | ||
// Arrange | ||
IDictionary<string, int> dictionary = null!; | ||
|
||
// Act | ||
Action action = () => dictionary.SetOptionalValue("key", new OptionalValue<int>(42)); | ||
|
||
// Assert | ||
ArgumentNullException exception = Assert.Throws<ArgumentNullException>(action); | ||
Assert.Equal("dictionary", exception.ParamName); | ||
} | ||
|
||
[Fact] | ||
public void ThrowsArgumentNullExceptionWhenKeyIsNull() | ||
{ | ||
// Arrange | ||
var dictionary = new Dictionary<string, int>(); | ||
|
||
// Act | ||
Action action = () => dictionary.SetOptionalValue(null!, new OptionalValue<int>(42)); | ||
|
||
// Assert | ||
ArgumentNullException exception = Assert.Throws<ArgumentNullException>(action); | ||
Assert.Equal("key", exception.ParamName); | ||
} | ||
} | ||
} |