Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify the CurrencyValue protocol #46

Merged
merged 3 commits into from
Jan 7, 2025
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
5 changes: 5 additions & 0 deletions Sources/Currency/CurrencyValue+Arithmetic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ extension CurrencyValue {

/// Subtracts the given other amount from the current exactAmount.
/// - Parameter amount: The other amount to subtract.
@inlinable
public mutating func subtract(_ amount: Self) {
self -= amount
}
Expand All @@ -128,6 +129,7 @@ extension CurrencyValue {

/// Subtracts the given other amount from the current exactAmount.
/// - Parameter amount: The other amount to subtract.
@inlinable
public mutating func subtract(_ amount: some BinaryInteger) {
self -= amount
}
Expand All @@ -144,6 +146,7 @@ extension CurrencyValue {

/// Subtracts the given other amount from the current exactAmount.
/// - Parameter amount: The other amount to subtract.
@inlinable
public mutating func subtract(_ amount: Decimal) {
self -= amount
}
Expand All @@ -170,6 +173,7 @@ extension CurrencyValue {

/// Multiplies the current exactAmount by the given other amount.
/// - Parameter amount: The other amount to multiply by.
@inlinable
public mutating func multiply(by amount: some BinaryInteger) {
self *= amount
}
Expand All @@ -186,6 +190,7 @@ extension CurrencyValue {

/// Multiplies the current exactAmount by the given other amount.
/// - Parameter amount: The other amount to multiply by.
@inlinable
public mutating func multiply(by amount: Decimal) {
self *= amount
}
Expand Down
2 changes: 2 additions & 0 deletions Sources/Currency/CurrencyValue+StringRepresentation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ extension CurrencyValue {
/// - Parameters:
/// - locale: The Locale to localize the value for. The default is `.current`, ig. the runtime environment's Locale.
/// - Returns: A localized String representation of the currency value.
@inlinable
public func localizedString(for locale: Locale = .current) -> String {
return "\(localize: self, for: locale)"
}
Expand Down Expand Up @@ -165,6 +166,7 @@ extension CurrencyValue {
/// - Parameters:
/// - formatter: The pre-configured formatter to use.
/// - Returns: A localized String representation of the currency value.
@inlinable
public func localizedString(using formatter: NumberFormatter) -> String {
return "\(localize: self, with: formatter)"
}
Expand Down
69 changes: 33 additions & 36 deletions Sources/Currency/CurrencyValue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,12 @@ public protocol CurrencyValue:
CustomStringConvertible, CustomDebugStringConvertible, CustomPlaygroundDisplayConvertible,
CustomReflectable,
Comparable, Hashable,
ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral
ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral,
AdditiveArithmetic
{
/// The information describing the currency.
static var descriptor: CurrencyDescriptor.Type { get }

/// The amount represented as a whole number of the curreny's "minor units".
///
/// For example, as the USD uses 1/100 for its minor unit,
/// with the value of `USD 1.01`, ``minorUnits`` will be the value `101`.
/// - Important: Fractional values (of the currency) are impossible to be represented in this value.
var minorUnits: CurrencyMinorUnitRepresentation { get }

/// The exact amount of money being represented,
/// even if the value is fractional of what the currency uses for its minor units.
///
Expand All @@ -50,51 +44,58 @@ public protocol CurrencyValue:
/// For an amount that is likely to be used in "every day" usage, see ``roundedAmount``
var exactAmount: Decimal { get }

/// The cannonical representation of the ``exactAmount`` of money this value represents,
/// rounded using the `bankers` method to the currency's ``CurrencyDescriptor/minorUnits``.
///
/// For example:
///
/// let usd = USD(10.007)
/// let yen = JPY(100.9)
/// let dinar = KWD(100.0019)
/// print(usd, yen, dinar)
/// // "USD(10.01), JPY(101), KWD(100.002)
var roundedAmount: Decimal { get }

/// Creates a representation of the currency with the exact value as given.
/// - Parameter exactAmount: The amount this instance should represent, without any rounding.
init(exactAmount: Decimal)
}

/// Creates a representation of the currency, converting the minor units representation to a decimal format.
///
/// e.g. If you provide the value `100` for a currency with `2` minor units, the `exactAmount` will be `1.00`.
/// - Parameter minorUnits: The minor units this value will represent.
init(minorUnits: CurrencyMinorUnitRepresentation)
// MARK: Defaults

/// Rounds the ``exactAmount`` of money being represented using the given rounding method
/// to the currency's ``CurrencyDescriptor/minorUnits``.
/// - Parameter roundingMode: The desired rounding mode to use on the original ``exactAmount``.
/// - Complexity: O(1)
/// - Returns: The rounded amount.
func roundedAmount(using roundingMode: NSDecimalNumber.RoundingMode) -> Decimal
extension CurrencyValue where Self: CurrencyDescriptor {
public static var descriptor: CurrencyDescriptor.Type { Self.self }
}

// MARK: Defaults
// MARK: Extensions

extension CurrencyValue {
/// The amount represented as a whole number of the curreny's "minor units".
///
/// For example, as the USD uses 1/100 for its minor unit,
/// with the value of `USD 1.01`, ``minorUnits`` will be the value `101`.
/// - Important: Fractional values of the currency are truncated.
public var minorUnits: CurrencyMinorUnitRepresentation {
let scaledAmount = self.exactAmount * Self.descriptor.minorUnitsCoefficient(for: .exactAmount)
return .init(scaledAmount.int64Value)
}

/// The "every day" representation of the ``exactAmount`` of money this value represents,
/// rounded using the `bankers` method to the currency's ``CurrencyDescriptor/minorUnits``.
///
/// For example:
/// ```swift
/// let usd = USD(10.007)
/// let yen = JPY(100.9)
/// let dinar = KWD(100.0019)
///
/// print(usd, yen, dinar)
/// // "USD(10.01), JPY(101), KWD(100.002)
/// ```
public var roundedAmount: Decimal { self.roundedAmount(using: .bankers) }

/// Creates a representation of the currency, converting the minor units representation to a decimal format.
///
/// e.g. If you provide the value `100` for a currency with `2` minor units, the `exactAmount` will be `1.00`.
/// - Parameter minorUnits: The minor units this value will represent.
public init(minorUnits: CurrencyMinorUnitRepresentation) {
let amount = Decimal(minorUnits) * Self.descriptor.minorUnitsCoefficient(for: .minorUnits)
self.init(exactAmount: amount)
}

/// Rounds the ``exactAmount`` of money being represented using the given rounding method
/// to the currency's ``CurrencyDescriptor/minorUnits``.
/// - Parameter roundingMode: The desired rounding mode to use on the original ``exactAmount``.
/// - Complexity: O(1)
/// - Returns: The rounded amount.
public func roundedAmount(using roundingMode: NSDecimalNumber.RoundingMode) -> Decimal {
var sourceAmount = self.exactAmount
var result = Decimal.zero
Expand All @@ -103,10 +104,6 @@ extension CurrencyValue {
}
}

extension CurrencyValue where Self: CurrencyDescriptor {
public static var descriptor: CurrencyDescriptor.Type { Self.self }
}

// MARK: Equatable

extension CurrencyValue {
Expand Down
Loading