From 5f4c5df6a004b4e906f5008e33668bcf5e1f033b Mon Sep 17 00:00:00 2001 From: Thomas Bruderer Date: Fri, 25 Aug 2023 10:31:06 +0200 Subject: [PATCH] use generic math for IBank --- Funcky.Money.Test/MoneyTest.cs | 2 +- Funcky.Money.Test/OneToOneBank.cs | 11 +++++++---- Funcky.Money/Bank/DefaultBank.cs | 16 +++++++++------- Funcky.Money/Bank/IBank.cs | 4 ++-- Funcky.Money/MoneyEvaluationContext.cs | 16 ++++++++-------- 5 files changed, 27 insertions(+), 22 deletions(-) diff --git a/Funcky.Money.Test/MoneyTest.cs b/Funcky.Money.Test/MoneyTest.cs index 44e0b26..74dd558 100644 --- a/Funcky.Money.Test/MoneyTest.cs +++ b/Funcky.Money.Test/MoneyTest.cs @@ -517,6 +517,6 @@ private static MoneyEvaluationContext OneToOneContext(Currency targetCurrency) .Builder .Default .WithTargetCurrency(targetCurrency) - .WithBank(OneToOneBank.Instance) + .WithBank(OneToOneBank.Instance) .Build(); } diff --git a/Funcky.Money.Test/OneToOneBank.cs b/Funcky.Money.Test/OneToOneBank.cs index d4e8711..09d41aa 100644 --- a/Funcky.Money.Test/OneToOneBank.cs +++ b/Funcky.Money.Test/OneToOneBank.cs @@ -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 : IBank + where TUnderlyingType : INumberBase { - public static readonly IBank Instance = new OneToOneBank(); + public static readonly IBank Instance = new OneToOneBank(); - public decimal ExchangeRate(Currency source, Currency target) - => 1m; + public TUnderlyingType ExchangeRate(Currency source, Currency target) + => TUnderlyingType.One; } diff --git a/Funcky.Money/Bank/DefaultBank.cs b/Funcky.Money/Bank/DefaultBank.cs index d9f3a17..72f91f7 100644 --- a/Funcky.Money/Bank/DefaultBank.cs +++ b/Funcky.Money/Bank/DefaultBank.cs @@ -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 : IBank + where TUnderlyingType : INumberBase { - internal static readonly DefaultBank Empty = new(); + internal static readonly DefaultBank 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 AddExchangeRate(Currency source, Currency target, TUnderlyingType sellRate) => new(ExchangeRates.Add((source, target), sellRate)); } diff --git a/Funcky.Money/Bank/IBank.cs b/Funcky.Money/Bank/IBank.cs index f1a34f9..cac8770 100644 --- a/Funcky.Money/Bank/IBank.cs +++ b/Funcky.Money/Bank/IBank.cs @@ -1,6 +1,6 @@ namespace Funcky; -public interface IBank +public interface IBank { - decimal ExchangeRate(Currency source, Currency target); + TUnderlyingType ExchangeRate(Currency source, Currency target); } diff --git a/Funcky.Money/MoneyEvaluationContext.cs b/Funcky.Money/MoneyEvaluationContext.cs index 10a3528..3665ce5 100644 --- a/Funcky.Money/MoneyEvaluationContext.cs +++ b/Funcky.Money/MoneyEvaluationContext.cs @@ -5,7 +5,7 @@ namespace Funcky; public sealed class MoneyEvaluationContext { - private MoneyEvaluationContext(Currency targetCurrency, Option distributionUnit, Option> roundingStrategy, IBank bank) + private MoneyEvaluationContext(Currency targetCurrency, Option distributionUnit, Option> roundingStrategy, IBank bank) { TargetCurrency = targetCurrency; DistributionUnit = distributionUnit; @@ -32,7 +32,7 @@ private MoneyEvaluationContext(Currency targetCurrency, Option distribu /// /// Source for exchange rates. /// - public IBank Bank { get; } + public IBank Bank { get; } public sealed class Builder { @@ -41,17 +41,17 @@ public sealed class Builder private readonly Option _targetCurrency; private readonly Option _distributionUnit; private readonly Option> _roundingStrategy; - private readonly IBank _bank; + private readonly IBank _bank; private Builder() { _targetCurrency = default; _roundingStrategy = default; _distributionUnit = default; - _bank = DefaultBank.Empty; + _bank = DefaultBank.Empty; } - private Builder(Option currency, Option distributionUnit, Option> roundingStrategy, IBank bank) + private Builder(Option currency, Option distributionUnit, Option> roundingStrategy, IBank bank) { _targetCurrency = currency; _distributionUnit = distributionUnit; @@ -71,17 +71,17 @@ public Builder WithRounding(IRoundingStrategy roundingStrategy) => With(roundingStrategy: Option.Some(roundingStrategy)); public Builder WithExchangeRate(Currency currency, decimal sellRate) - => _bank is DefaultBank bank + => _bank is DefaultBank 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 bank) => With(bank: Option.Some(bank)); public Builder WithSmallestDistributionUnit(decimal distributionUnit) => With(distributionUnit: distributionUnit); - private Builder With(Option targetCurrency = default, Option distributionUnit = default, Option> roundingStrategy = default, Option bank = default) + private Builder With(Option targetCurrency = default, Option distributionUnit = default, Option> roundingStrategy = default, Option> bank = default) => new( targetCurrency.OrElse(_targetCurrency), distributionUnit.OrElse(_distributionUnit),