Skip to content

Commit

Permalink
use generic math for IBank
Browse files Browse the repository at this point in the history
  • Loading branch information
FreeApophis committed Aug 25, 2023
1 parent c53dca7 commit 5f4c5df
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 22 deletions.
2 changes: 1 addition & 1 deletion Funcky.Money.Test/MoneyTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,6 @@ private static MoneyEvaluationContext OneToOneContext(Currency targetCurrency)
.Builder
.Default
.WithTargetCurrency(targetCurrency)
.WithBank(OneToOneBank.Instance)
.WithBank(OneToOneBank<decimal>.Instance)
.Build();
}
11 changes: 7 additions & 4 deletions Funcky.Money.Test/OneToOneBank.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
using System.Numerics;

namespace Funcky.Test;

// Bank which returns a 1:1 exchange rate for every pair of currencies.
internal sealed class OneToOneBank : IBank
internal sealed class OneToOneBank<TUnderlyingType> : IBank<TUnderlyingType>
where TUnderlyingType : INumberBase<TUnderlyingType>
{
public static readonly IBank Instance = new OneToOneBank();
public static readonly IBank<TUnderlyingType> Instance = new OneToOneBank<TUnderlyingType>();

public decimal ExchangeRate(Currency source, Currency target)
=> 1m;
public TUnderlyingType ExchangeRate(Currency source, Currency target)
=> TUnderlyingType.One;
}
16 changes: 9 additions & 7 deletions Funcky.Money/Bank/DefaultBank.cs
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
using System.Collections.Immutable;
using System.Numerics;
using Funcky.Extensions;

namespace Funcky;

internal sealed class DefaultBank : IBank
internal sealed class DefaultBank<TUnderlyingType> : IBank<TUnderlyingType>
where TUnderlyingType : INumberBase<TUnderlyingType>
{
internal static readonly DefaultBank Empty = new();
internal static readonly DefaultBank<TUnderlyingType> Empty = new();

public DefaultBank(ImmutableDictionary<(Currency Source, Currency Target), decimal> exchangeRates)
public DefaultBank(ImmutableDictionary<(Currency Source, Currency Target), TUnderlyingType> exchangeRates)
{
ExchangeRates = exchangeRates;
}

private DefaultBank()
{
ExchangeRates = ImmutableDictionary<(Currency Source, Currency Target), decimal>.Empty;
ExchangeRates = ImmutableDictionary<(Currency Source, Currency Target), TUnderlyingType>.Empty;
}

public ImmutableDictionary<(Currency Source, Currency Target), decimal> ExchangeRates { get; }
public ImmutableDictionary<(Currency Source, Currency Target), TUnderlyingType> ExchangeRates { get; }

public decimal ExchangeRate(Currency source, Currency target)
public TUnderlyingType ExchangeRate(Currency source, Currency target)
=> ExchangeRates
.GetValueOrNone(key: (source, target))
.GetOrElse(() => throw new MissingExchangeRateException($"No exchange rate for {source.AlphabeticCurrencyCode} => {target.AlphabeticCurrencyCode}."));

internal DefaultBank AddExchangeRate(Currency source, Currency target, decimal sellRate)
internal DefaultBank<TUnderlyingType> AddExchangeRate(Currency source, Currency target, TUnderlyingType sellRate)
=> new(ExchangeRates.Add((source, target), sellRate));
}
4 changes: 2 additions & 2 deletions Funcky.Money/Bank/IBank.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Funcky;

public interface IBank
public interface IBank<TUnderlyingType>
{
decimal ExchangeRate(Currency source, Currency target);
TUnderlyingType ExchangeRate(Currency source, Currency target);
}
16 changes: 8 additions & 8 deletions Funcky.Money/MoneyEvaluationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Funcky;

public sealed class MoneyEvaluationContext
{
private MoneyEvaluationContext(Currency targetCurrency, Option<decimal> distributionUnit, Option<IRoundingStrategy<decimal>> roundingStrategy, IBank bank)
private MoneyEvaluationContext(Currency targetCurrency, Option<decimal> distributionUnit, Option<IRoundingStrategy<decimal>> roundingStrategy, IBank<decimal> bank)
{
TargetCurrency = targetCurrency;
DistributionUnit = distributionUnit;
Expand All @@ -32,7 +32,7 @@ private MoneyEvaluationContext(Currency targetCurrency, Option<decimal> distribu
/// <summary>
/// Source for exchange rates.
/// </summary>
public IBank Bank { get; }
public IBank<decimal> Bank { get; }

public sealed class Builder
{
Expand All @@ -41,17 +41,17 @@ public sealed class Builder
private readonly Option<Currency> _targetCurrency;
private readonly Option<decimal> _distributionUnit;
private readonly Option<IRoundingStrategy<decimal>> _roundingStrategy;
private readonly IBank _bank;
private readonly IBank<decimal> _bank;

private Builder()
{
_targetCurrency = default;
_roundingStrategy = default;
_distributionUnit = default;
_bank = DefaultBank.Empty;
_bank = DefaultBank<decimal>.Empty;
}

private Builder(Option<Currency> currency, Option<decimal> distributionUnit, Option<IRoundingStrategy<decimal>> roundingStrategy, IBank bank)
private Builder(Option<Currency> currency, Option<decimal> distributionUnit, Option<IRoundingStrategy<decimal>> roundingStrategy, IBank<decimal> bank)
{
_targetCurrency = currency;
_distributionUnit = distributionUnit;
Expand All @@ -71,17 +71,17 @@ public Builder WithRounding(IRoundingStrategy<decimal> roundingStrategy)
=> With(roundingStrategy: Option.Some(roundingStrategy));

public Builder WithExchangeRate(Currency currency, decimal sellRate)
=> _bank is DefaultBank bank
=> _bank is DefaultBank<decimal> bank
? With(bank: bank.AddExchangeRate(currency, GetTargetCurrencyOrException(), sellRate))
: throw new InvalidMoneyEvaluationContextBuilderException("You can either use WithExchangeRate or WithBank, but not both.");

public Builder WithBank(IBank bank)
public Builder WithBank(IBank<decimal> bank)
=> With(bank: Option.Some(bank));

public Builder WithSmallestDistributionUnit(decimal distributionUnit)
=> With(distributionUnit: distributionUnit);

private Builder With(Option<Currency> targetCurrency = default, Option<decimal> distributionUnit = default, Option<IRoundingStrategy<decimal>> roundingStrategy = default, Option<IBank> bank = default)
private Builder With(Option<Currency> targetCurrency = default, Option<decimal> distributionUnit = default, Option<IRoundingStrategy<decimal>> roundingStrategy = default, Option<IBank<decimal>> bank = default)
=> new(
targetCurrency.OrElse(_targetCurrency),
distributionUnit.OrElse(_distributionUnit),
Expand Down

0 comments on commit 5f4c5df

Please sign in to comment.