Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ indent_size = 4
indent_style = space
indent_size = 2

[*.json]
indent_size = 2

[*.{xml,csproj,slnx}]
indent_size = 2

[*.cs]
dotnet_diagnostic.CA1034.severity = none

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.406
global-json-path: ${{ github.workspace }}/global.json
#- name: Info
# run: dotnet --info
- name: Restore
Expand Down
2 changes: 1 addition & 1 deletion archive/NMoneys.Exchange/NMoneys.Exchange.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

Expand Down
10 changes: 10 additions & 0 deletions global.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"sdk": {
"version": "10.0.202",
"allowPrerelease": false,
"rollForward": "latestFeature"
},
"test": {
"runner": "VSTest"
}
}
4 changes: 2 additions & 2 deletions scripts/NMoneys.build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ task Clean {
exec { & dotnet clean -c $configuration -v $verbosity --nologo $BASE_DIR }
# clear built packages
Join-Path $BASE_DIR src NMoneys bin $configuration |
Get-ChildItem |
Get-ChildItem -ErrorAction SilentlyContinue |
Where-Object { $_.Name -match '.nupkg' } |
Remove-Item
# clear release folder
Expand All @@ -44,7 +44,7 @@ task Test {
& dotnet run --no-build -c $configuration -v $verbosity --project $tests_dir -- `
--results-directory $test_results_dir `
--coverage --coverage-output-format cobertura --coverage-output "$test_project.cobertura.xml" `
--settings $runsettings_path
--settings $runsettings_path
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/NMoneys.Serialization/NMoneys.Serialization.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@

<PropertyGroup>
<Version>5.0.0</Version>
<AssemblyVersion>5.0.0.0</AssemblyVersion>
<FileVersion>5.0.0.0</FileVersion>
<AssemblyVersion>$(Version).0</AssemblyVersion>
<FileVersion>$(Version).0</FileVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
8 changes: 4 additions & 4 deletions src/NMoneys/Currency.Creation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ public static Currency Get(RegionInfo region)
/// <exception cref="MisconfiguredCurrencyException">The currency represented by <paramref name="code" />
/// has not been properly configured by the library implementor. Please, log a issue.</exception>
[Pure]
public static bool TryGet(CurrencyIsoCode code, out Currency? currency)
public static bool TryGet(CurrencyIsoCode code, [NotNullWhen(true)] out Currency? currency)
{
bool tryGet = false;
currency = null;
Expand Down Expand Up @@ -168,7 +168,7 @@ public static bool TryGet(CurrencyIsoCode code, out Currency? currency)
/// lookup succeeds, or null if the lookup fails.</param>
/// <returns>true if <paramref name="threeLetterIsoSymbol"/> was looked up successfully; otherwise, false.</returns>
[Pure]
public static bool TryGet(string threeLetterIsoSymbol, out Currency? currency)
public static bool TryGet(string threeLetterIsoSymbol, [NotNullWhen(true)] out Currency? currency)
{
bool tryGet = false;
currency = null;
Expand Down Expand Up @@ -202,7 +202,7 @@ public static bool TryGet(string threeLetterIsoSymbol, out Currency? currency)
/// the <paramref name="culture"/> if the lookup succeeds, or null if the lookup fails.</param>
/// <returns>true if <paramref name="currency"/> was looked up successfully; otherwise, false.</returns>
[Pure]
public static bool TryGet([NotNull] CultureInfo culture, out Currency? currency)
public static bool TryGet([NotNull] CultureInfo culture, [NotNullWhen(true)] out Currency? currency)
{
bool tryGet = false;
currency = null;
Expand Down Expand Up @@ -235,7 +235,7 @@ public static bool TryGet([NotNull] CultureInfo culture, out Currency? currency)
/// the <paramref name="region"/> if the lookup succeeds, or null if the lookup fails.</param>
/// <returns>true if <paramref name="currency"/> was looked up successfully; otherwise, false.</returns>
[Pure]
public static bool TryGet([NotNull] RegionInfo region, out Currency? currency)
public static bool TryGet([NotNull] RegionInfo region, [NotNullWhen(true)] out Currency? currency)
{
return TryGet(region.ISOCurrencySymbol, out currency);
}
Expand Down
5 changes: 3 additions & 2 deletions src/NMoneys/CurrencyIsoCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,13 +186,14 @@ public enum CurrencyIsoCode : ushort
/// <summary>
/// Bulgarian Lev
/// </summary>
[CanonicalCulture("bg-BG")]
[CanonicalCulture("bg-BG"), Obsolete("deprecated in favor of EUR")]
[Info(
englishName: "Bulgarian Lev", nativeName: "Български лев", symbol: "лв.",
significantDecimalDigits: 2,
decimalSeparator: ",",
groupSeparator: "\u00a0", groupSizes: new byte[] { 0 },
positivePattern: 3, negativePattern: 8
positivePattern: 3, negativePattern: 8,
isObsolete: true
)]
BGN = 975,

Expand Down
86 changes: 52 additions & 34 deletions src/NMoneys/Money.Parsing.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;

Expand All @@ -8,18 +9,22 @@ public partial struct Money
/// <summary>
/// Converts the string representation of a monetary quantity (<see cref="Money.AsQuantity()"/>) to its <see cref="Money"/> equivalent.
/// </summary>
/// <param name="quantity">Monetary quantity representation to be parsed.</param>
/// <exception cref="ArgumentException">The currency value does not contain <see cref="CurrencyIsoCode"/> enumeration information.</exception>
/// <exception cref="FormatException">The amount value is not in the correct <see cref="Decimal"/> format.</exception>
/// <exception cref="UndefinedCodeException">The currency value does not contain a defined <see cref="CurrencyIsoCode"/>.</exception>
/// <exception cref="ArgumentOutOfRangeException">Components of the representation are missing.</exception>
/// <returns>Monetary quantity equivalent to its representation.</returns>
[Pure]
public static Money Parse(string quantity)
public static Money Parse(string quantity) => Parse(quantity.AsSpan());

/// <inheritdoc cref="Parse(string)"/>
[Pure]
public static Money Parse(ReadOnlySpan<char> quantity)
{
ReadOnlySpan<char> span = quantity.AsSpan();
CurrencyIsoCode currency = Enum.Parse<CurrencyIsoCode>(span[..3], ignoreCase: false);
CurrencyIsoCode currency = Enum.Parse<CurrencyIsoCode>(quantity[..3], ignoreCase: false);
currency.AssertDefined();
decimal amount = decimal.Parse(span[4..],
decimal amount = decimal.Parse(quantity[4..],
style: NumberStyles.Float,
provider: NumberFormatInfo.InvariantInfo);
return new Money(amount, currency);
Expand All @@ -33,25 +38,23 @@ public static Money Parse(string quantity)
/// if the conversion succeeded, or <c>null</c> if the conversion failed.</param>
/// <returns><c>true</c> if <paramref name="quantity"/> was converted successfully; otherwise, <c>false</c>.</returns>
[Pure]
public static bool TryParse(string quantity, out Money? money)
public static bool TryParse(string quantity, [NotNullWhen(true)] out Money? money) =>
TryParse(quantity.AsSpan(), out money);

/// <inheritdoc cref="TryParse(string, out Money?)"/>
[Pure]
public static bool TryParse(ReadOnlySpan<char> quantity, [NotNullWhen(true)] out Money? money)
{
ReadOnlySpan<char> span = quantity.AsSpan();
try
if (quantity.Length >= 5)
{
if (Enum.TryParse(span[..3], out CurrencyIsoCode currencyCode) &&
if (Enum.TryParse(quantity[..3], out CurrencyIsoCode currencyCode) &&
currencyCode.CheckDefined() &&
decimal.TryParse(span[4..], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out decimal amount))
decimal.TryParse(quantity[4..], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out decimal amount))
{
money = new Money(amount, currencyCode);
return true;
}
}
#pragma warning disable CA1031
catch
{
// span indexing operation can throw
}
#pragma warning restore CA1031

money = null;
return false;
Expand All @@ -70,10 +73,11 @@ public static bool TryParse(string quantity, out Money? money)
/// <exception cref="ArgumentNullException"><paramref name="s"/> is null.</exception>
/// <seealso cref="decimal.Parse(string, NumberStyles, IFormatProvider)" />
[Pure]
public static Money Parse(string s, Currency currency)
{
return Parse(s, NumberStyles.Currency, currency);
}
public static Money Parse(string s, Currency currency) => Parse(s, NumberStyles.Currency, currency);

/// <inheritdoc cref="Parse(string, Currency)"/>
[Pure]
public static Money Parse(ReadOnlySpan<char> s, Currency currency) => Parse(s, NumberStyles.Currency, currency);

/// <summary>
/// Converts the string representation of a monetary quantity to its <see cref="Money"/> equivalent
Expand All @@ -94,7 +98,11 @@ public static Money Parse(string s, Currency currency)
/// </exception>
/// <seealso cref="decimal.Parse(string, NumberStyles, IFormatProvider)" />
[Pure]
public static Money Parse(string s, NumberStyles style, Currency currency)
public static Money Parse(string s, NumberStyles style, Currency currency) => Parse(s.AsSpan(), style, currency);

/// <inheritdoc cref="Parse(string, NumberStyles, Currency)"/>
[Pure]
public static Money Parse(ReadOnlySpan<char> s, NumberStyles style, Currency currency)
{
decimal amount = decimal.Parse(s, style, currency);

Expand All @@ -116,10 +124,13 @@ public static Money Parse(string s, NumberStyles style, Currency currency)
/// <returns>true if s was converted successfully; otherwise, false.</returns>
/// <seealso cref="decimal.TryParse(string, NumberStyles, IFormatProvider, out decimal)" />
[Pure]
public static bool TryParse(string s, Currency currency, out Money? money)
{
return TryParse(s, NumberStyles.Currency, currency, out money);
}
public static bool TryParse(string s, Currency currency, [NotNullWhen(true)] out Money? money) =>
TryParse(s, NumberStyles.Currency, currency, out money);

/// <inheritdoc cref="TryParse(string, Currency, out Money?)"/>
[Pure]
public static bool TryParse(ReadOnlySpan<char> s, Currency currency, [NotNullWhen(true)] out Money? money) =>
TryParse(s, NumberStyles.Currency, currency, out money);

/// <summary>
/// Converts the string representation of a monetary quantity to its <see cref="Money"/> equivalent
Expand All @@ -143,9 +154,15 @@ public static bool TryParse(string s, Currency currency, out Money? money)
/// <exception cref="ArgumentNullException"><paramref name="currency"/> is <c>null</c>.</exception>
/// <seealso cref="decimal.TryParse(string, NumberStyles, IFormatProvider, out decimal)" />
[Pure]
public static bool TryParse(string s, NumberStyles style, Currency currency, out Money? money)
public static bool TryParse(string s, NumberStyles style, Currency currency,
[NotNullWhen(true)] out Money? money) =>
TryParse(s.AsSpan(), style, currency, out money);

/// <inheritdoc cref="TryParse(string, NumberStyles, Currency, out Money?)"/>
[Pure]
public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, Currency currency,
[NotNullWhen(true)] out Money? money)
{
ArgumentNullException.ThrowIfNull(currency, nameof(currency));
money = tryParse(s, style, currency.FormatInfo, currency.IsoCode);
return money.HasValue;
}
Expand Down Expand Up @@ -178,14 +195,16 @@ public static bool TryParse(string s, NumberStyles style, Currency currency, out
/// This parameter is passed uninitialized.
/// </param>
/// <returns> true if s was converted successfully; otherwise, false. </returns>
/// <exception cref="ArgumentNullException"><paramref name="numberFormatInfo"/> or <paramref name="currency"/> is <c>null</c>.</exception>
[Pure]
public static bool TryParse(string s, NumberStyles style, NumberFormatInfo numberFormatInfo, Currency currency,
out Money? money)
{
ArgumentNullException.ThrowIfNull(numberFormatInfo, nameof(numberFormatInfo));
ArgumentNullException.ThrowIfNull(currency, nameof(currency));
[NotNullWhen(true)] out Money? money) => TryParse(s.AsSpan(), style, numberFormatInfo, currency, out money);

/// <inheritdoc cref="TryParse(string, NumberStyles, NumberFormatInfo, Currency, out Money?)"/>
[Pure]
public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo numberFormatInfo,
Currency currency,
[NotNullWhen(true)] out Money? money)
{
var mergedNumberFormatInfo = (NumberFormatInfo)numberFormatInfo.Clone();
mergedNumberFormatInfo.CurrencySymbol = currency.Symbol;

Expand All @@ -194,11 +213,10 @@ public static bool TryParse(string s, NumberStyles style, NumberFormatInfo numbe
return money.HasValue;
}

private static Money? tryParse(string s, NumberStyles style, NumberFormatInfo formatProvider,
private static Money? tryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo formatProvider,
CurrencyIsoCode currency)
{
decimal amount;
bool result = decimal.TryParse(s, style, formatProvider, out amount);
bool result = decimal.TryParse(s, style, formatProvider, out var amount);
return result ? new Money(amount, currency) : null;
}
}
Loading
Loading