Skip to content

Commit

Permalink
.NET Standard 2.0 support, tests and cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
mikernet committed Mar 8, 2024
1 parent f9f3e5f commit a5df4c4
Show file tree
Hide file tree
Showing 15 changed files with 292 additions and 177 deletions.
3 changes: 2 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<Project>
<PropertyGroup>
<LangVersion>12.0</LangVersion>
<Nullable>enable</Nullable>
<Nullable Condition="'$(TargetFramework)' != 'netstandard2.0'">enable</Nullable>
<Nullable Condition="'$(TargetFramework)' == 'netstandard2.0'">annotations</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>true</GenerateDocumentationFile>

Expand Down
7 changes: 7 additions & 0 deletions Singulink.Globalization.Currency.sln
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{764A
.github\dependabot.yml = .github\dependabot.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Singulink.Globalization.Currency.NetStandardTests", "Tests\Singulink.Globalization.Currency.NetStandardTests\Singulink.Globalization.Currency.NetStandardTests.csproj", "{A95689C0-68A0-4457-9B2C-0357468920AE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -55,6 +57,10 @@ Global
{FE384100-51BA-42C4-B269-F0C3BCF5A6BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FE384100-51BA-42C4-B269-F0C3BCF5A6BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FE384100-51BA-42C4-B269-F0C3BCF5A6BE}.Release|Any CPU.Build.0 = Release|Any CPU
{A95689C0-68A0-4457-9B2C-0357468920AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A95689C0-68A0-4457-9B2C-0357468920AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A95689C0-68A0-4457-9B2C-0357468920AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A95689C0-68A0-4457-9B2C-0357468920AE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -64,6 +70,7 @@ Global
{FE384100-51BA-42C4-B269-F0C3BCF5A6BE} = {DBA8BA65-D53C-41A8-9534-EAB6AE159CCB}
{97D96E55-E2D9-4076-8D71-BA319A703C06} = {DBA8BA65-D53C-41A8-9534-EAB6AE159CCB}
{4028E49D-6194-47FA-AEA9-5B046F8013D2} = {2CD9744C-7FC1-485B-BB89-406676D56D25}
{A95689C0-68A0-4457-9B2C-0357468920AE} = {2CD9744C-7FC1-485B-BB89-406676D56D25}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {06D2E21A-73BC-4E56-B817-7147C8D79C05}
Expand Down
37 changes: 25 additions & 12 deletions Source/Singulink.Globalization.Currency/Currency.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class Currency : IFormattable
public Money MinorUnit => new Money(new decimal(1, 0, 0, false, (byte)DecimalDigits), this);

/// <summary>
/// Gets a list containing language identifers and currency names.
/// Gets a list containing language identifiers and currency names.
/// </summary>
public IEnumerable<(string CultureName, string Name)> LocalizedNames => _localizedNameLookup?.Select(kvp => (kvp.Key, kvp.Value)) ?? Array.Empty<(string, string)>();

Expand All @@ -54,7 +54,7 @@ public class Currency : IFormattable
/// is not found.
/// </remarks>
public Currency(string name, string currencyCode, string symbol, int decimalDigits, params (string CultureName, string Name)[] localizedNames)
: this(name, currencyCode, symbol, decimalDigits, localizedNames.Length == 0 ? null : localizedNames.AsEnumerable()) { }
: this(name, currencyCode, symbol, decimalDigits, localizedNames.Length is 0 ? null : localizedNames.AsEnumerable()) { }

/// <inheritdoc cref="Currency(string, string, string, int, ValueTuple{string, string}[])"/>
public Currency(string name, string currencyCode, string symbol, int decimalDigits, IEnumerable<(string CultureName, string Name)>? localizedNames = null)
Expand All @@ -63,16 +63,16 @@ public Currency(string name, string currencyCode, string symbol, int decimalDigi
name = name.Trim();
symbol = symbol.Trim();

if (currencyCode.Length == 0)
if (currencyCode.Length is 0)
throw new ArgumentException("Currency code is required.", nameof(currencyCode));

if (currencyCode.Length > 20)
throw new ArgumentOutOfRangeException(nameof(currencyCode), "Currency code has a maximum length of 20 characters.");

if (name.Length == 0)
if (name.Length is 0)
throw new ArgumentException("Name is required.", nameof(name));

if (symbol.Length == 0)
if (symbol.Length is 0)
throw new ArgumentException("Symbol is required.", nameof(symbol));

if (symbol.Length > 20)
Expand All @@ -86,14 +86,23 @@ public Currency(string name, string currencyCode, string symbol, int decimalDigi
Symbol = symbol;
DecimalDigits = decimalDigits;

if (localizedNames != null)
if (localizedNames is not null)
{
foreach (var (cultureName, localName) in localizedNames)
foreach (var localizedName in localizedNames)
{
string cultureName = CoerceCultureName(localizedName.CultureName);
string localName = CoerceCurrencyName(localizedName.Name);

_localizedNameLookup ??= new(StringComparer.OrdinalIgnoreCase);
#if NETSTANDARD
if (_localizedNameLookup.ContainsKey(cultureName))
throw new ArgumentException($"Duplicate culture name '{cultureName}' in localized names.", nameof(localizedNames));

if (!_localizedNameLookup.TryAdd(CoerceCultureName(cultureName), CoerceCurrencyName(localName)))
_localizedNameLookup.Add(cultureName, localName);
#else
if (!_localizedNameLookup.TryAdd(cultureName, localName))
throw new ArgumentException($"Duplicate culture name '{cultureName}' in localized names.", nameof(localizedNames));
#endif
}
}

Expand Down Expand Up @@ -159,15 +168,15 @@ public string ToString(string? format, CultureInfo? culture = null)

string name;

if (_localizedNameLookup == null)
if (_localizedNameLookup is null)
{
name = Name;
}
else if (!_localizedNameLookup.TryGetValue(culture.Name, out name))
{
var neutralCulture = culture.GetNeutralCulture();

if (neutralCulture == null || !_localizedNameLookup.TryGetValue(neutralCulture.Name, out name))
if (neutralCulture is null || !_localizedNameLookup.TryGetValue(neutralCulture.Name, out name))
name = Name;
}

Expand Down Expand Up @@ -220,9 +229,13 @@ internal static CurrencyRegistry CreateSystemRegistry()

if (localizedNameLookup.TryGetValue(localizationCultureName, out string existingLocalizedName) && existingLocalizedName != localizedName)
{
// This shouldn't happen, but if the data changes and it does then make this future-proof by adding the localized name to the specific
// culture instead to override the different neutral culture name that was set.
// This shouldn't happen in .NET+, but if the data changes and it does then make this future-proof by adding the localized name to the
// specific culture instead to override the different neutral culture name that was set.

// This *does* happen on .NET Framework, so neutral culture name will be whatever specific culture name was first.
#if !NETSTANDARD
Debug.Fail("Neutral localization culture name was already set to a different name.");
#endif
localizedNameLookup.Add(culture.Name, localizedName);
}
else
Expand Down
22 changes: 19 additions & 3 deletions Source/Singulink.Globalization.Currency/CurrencyRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ namespace Singulink.Globalization;
/// <summary>
/// Represents a collection of currencies.
/// </summary>
public sealed class CurrencyRegistry : ISet<Currency>, IReadOnlySet<Currency>
public sealed class CurrencyRegistry : ISet<Currency>
#if !NETSTANDARD
#pragma warning disable SA1001 // Commas should be spaced correctly
, IReadOnlySet<Currency>
#pragma warning restore SA1001
#endif
{
private static CurrencyRegistry? _default;
private static CurrencyRegistry? _system;
Expand All @@ -25,16 +30,27 @@ public CurrencyRegistry(string name, IEnumerable<Currency> currencies)
{
_name = name.Trim();

if (_name.Length == 0)
if (_name.Length is 0)
throw new ArgumentException("Name is required.", nameof(name));

_currencies = [];
_currencyLookup = new(StringComparer.OrdinalIgnoreCase);

foreach (var currency in currencies)
{
#if NETSTANDARD
if (_currencies.Add(currency))
{
if (_currencyLookup.ContainsKey(currency.CurrencyCode))
throw new ArgumentException($"Multiple currencies with currency code '{currency.CurrencyCode}'.", nameof(currencies));

_currencyLookup.Add(currency.CurrencyCode, currency);
}
#else
if (_currencies.Add(currency) && !_currencyLookup.TryAdd(currency.CurrencyCode, currency))
throw new ArgumentException($"Multiple currencies with currency code '{currency.CurrencyCode}'.", nameof(currencies));
#endif

}
}

Expand All @@ -51,7 +67,7 @@ public static CurrencyRegistry Default
{
get => _default ??= System;
set {
if (_default != null)
if (_default is not null)
throw new InvalidOperationException("Default currency registry cannot be set after it has already been set or accessed.");

_default = value;
Expand Down
27 changes: 0 additions & 27 deletions Source/Singulink.Globalization.Currency/IImmutableMoneySet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,31 +153,4 @@ public interface IImmutableMoneySet : ICollection<Money>, IReadOnlyMoneySet
/// Removes all zero amounts from this set and returns the resulting set.
/// </summary>
public IImmutableMoneySet TrimZeroAmounts();

#region Explicit Interface Implementations

/// <summary>
/// Gets a value indicating whether the set is read-only. Always returns <see langword="true"/>.
/// </summary>
bool ICollection<Money>.IsReadOnly => true;

/// <summary>
/// Not supported.
/// </summary>
/// <exception cref="NotSupportedException">This operation is not supported.</exception>
void ICollection<Money>.Add(Money item) => throw new NotSupportedException();

/// <summary>
/// Not supported.
/// </summary>
/// <exception cref="NotSupportedException">This operation is not supported.</exception>
void ICollection<Money>.Clear() => throw new NotSupportedException();

/// <summary>
/// Not supported.
/// </summary>
/// <exception cref="NotSupportedException">This operation is not supported.</exception>
bool ICollection<Money>.Remove(Money item) => throw new NotSupportedException();

#endregion
}
Loading

0 comments on commit a5df4c4

Please sign in to comment.