Documentation
¶
Overview ¶
Package fulus provides utilities for handling currency conversion and calculations.
Fulus helps developers work with different currencies and monetary operations in a type-safe and efficient manner. It includes features for currency parsing, formatting, conversion, and basic arithmetic operations while maintaining precision and correctness.
The package supports:
- type-safe arithmetic across currency types (Add, Sub, Mul, Div, Abs, Neg)
- configurable conversion rounding policies
- decimal string parsing with minor-unit validation
- JSON and database/sql interoperability
Index ¶
- Variables
- func Convert[F, T currency.Currency](m Money[F], ratio Ratio[F, T], mode RoundingMode) (Money[T], ConversionResult[F, T], error)
- type Allocation
- type ConversionResult
- type Distribution
- type Money
- func (m Money[T]) Abs() (Money[T], error)
- func (m Money[T]) Add(other Money[T]) (Money[T], error)
- func (m Money[T]) Allocate(ratios []int64) (Allocation[T], error)
- func (m Money[T]) Amount() int64
- func (m Money[T]) Cmp(other Money[T]) int
- func (m Money[T]) Distribute(chunks int64) (Distribution, error)
- func (m Money[T]) Div(divisor int64, mode RoundingMode) (Money[T], error)
- func (m Money[T]) Equal(other Money[T]) bool
- func (m Money[T]) Format(locale locale.Locale) string
- func (m Money[T]) GreaterThan(other Money[T]) bool
- func (m Money[T]) GreaterThanOrEqual(other Money[T]) bool
- func (m Money[T]) IsNegative() bool
- func (m Money[T]) IsPositive() bool
- func (m Money[T]) IsZero() bool
- func (m Money[T]) LessThan(other Money[T]) bool
- func (m Money[T]) LessThanOrEqual(other Money[T]) bool
- func (m Money[T]) MarshalJSON() ([]byte, error)
- func (m Money[T]) Mul(scale int64) (Money[T], error)
- func (m Money[T]) MustAdd(other Money[T]) Money[T]
- func (m Money[T]) MustMul(scale int64) Money[T]
- func (m Money[T]) MustSub(other Money[T]) Money[T]
- func (m Money[T]) Neg() (Money[T], error)
- func (m *Money[T]) Scan(value any) error
- func (m Money[T]) String() string
- func (m Money[T]) Sub(other Money[T]) (Money[T], error)
- func (m *Money[T]) UnmarshalJSON(data []byte) error
- func (m Money[T]) Validate(min, max int64) error
- func (m Money[T]) Value() (driver.Value, error)
- type Ratio
- type RoundingMode
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrValidation is the error returned when money validation fails ErrValidation = errors.New("money validation error") // ErrOverflow indicates an arithmetic operation would overflow ErrOverflow = errors.New("arithmetic operation would overflow") // ErrInvalidChunks indicates an invalid number of chunks for distribution ErrInvalidChunks = errors.New("number of chunks must be positive") // ErrZeroDenominator indicates division by zero in conversion ErrZeroDenominator = errors.New("denominator cannot be zero") // ErrDivisionByZero indicates division by zero ErrDivisionByZero = errors.New("division by zero") // ErrNoRatios indicates no ratios were provided for allocation ErrNoRatios = errors.New("no ratios provided") // ErrNegativeOrZeroRatios indicates negative ratios in allocation ErrNegativeOrZeroRatios = errors.New("ratios must be positive") // ErrInvalidRoundingMode indicates unsupported rounding mode ErrInvalidRoundingMode = errors.New("invalid rounding mode") // ErrInvalidAmountFormat indicates amount string cannot be parsed ErrInvalidAmountFormat = errors.New("invalid amount format") // ErrScaleMismatch indicates decimal digits exceed currency minor units ErrScaleMismatch = errors.New("amount scale exceeds currency minor units") // ErrInvalidExchangeRate indicates an invalid exchange rate format or value ErrInvalidExchangeRate = errors.New("invalid exchange rate") )
var (
DefaultLocale = locale.EN
)
DefaultLocale is the fallback locale when none is specified
Functions ¶
func Convert ¶
func Convert[F, T currency.Currency](m Money[F], ratio Ratio[F, T], mode RoundingMode) (Money[T], ConversionResult[F, T], error)
Convert performs conversion with an explicit rounding strategy. The ratio should be provided as (numerator, denominator) representing numerator/denominator. Returns both the converted Money value and the actual ratio used after rounding.
Example ¶
eur := NewMoney[currency.EUR](500) // €5.00
ratio := Ratio[currency.EUR, currency.USD]{
Numerator: 104565, // 1.04565 represented as 104565/100000
Denominator: 100000,
}
eurInUsd, _, err := Convert(eur, ratio, RoundTruncate)
if err != nil {
panic(err)
}
fmt.Println(eurInUsd)
Output: $5.22
Types ¶
type Allocation ¶
Allocation represents how money is divided according to ratios
type ConversionResult ¶
type ConversionResult[F currency.Currency, T currency.Currency] struct { // Amount stores the resulting converted monetary value Amount int64 // ActualRate stores the precise conversion rate that was actually applied ActualRate Ratio[F, T] }
ConversionResult holds both the converted amount and the actual ratio used
type Distribution ¶
type Distribution struct {
// SmallerChunkSize represents the value of the smaller portions in the distribution
SmallerChunkSize int64
// SmallerCount represents how many smaller chunks are in the distribution
SmallerCount int64
// LargerChunkSize represents the value of the larger portions in the distribution
LargerChunkSize int64
// LargerCount represents how many larger chunks are in the distribution
LargerCount int64
}
Distribution represents how to split money into chunks
type Money ¶
type Money[T currency.Currency] struct { // Currency represents the type of currency for this money value currency.Currency // contains filtered or unexported fields }
Money represents a monetary value in a specific currency.
func NewMoney ¶
NewMoney creates a new Money instance with the given amount and currency. The amount should be specified in the currency's smallest sub-unit (e.g., cents for USD, pence for GBP). For example: USD 10.50 should be passed as 1050 EUR 5.99 should be passed as 599
func ParseMoney ¶
ParseMoney parses a canonical decimal amount into Money using the currency's minor units. Supported format: optional sign (+/-), digits, optional decimal point and digits.
func (Money[T]) Abs ¶
Abs returns the absolute value of the money amount. Returns ErrOverflow if the amount is math.MinInt64.
func (Money[T]) Add ¶
Add performs addition of two Money values of the same currency. Returns ErrOverflow if the operation would overflow int64.
func (Money[T]) Allocate ¶
func (m Money[T]) Allocate(ratios []int64) (Allocation[T], error)
Allocate divides money according to provided ratios
Example ¶
dollars := NewMoney[currency.USD](10000) // 100.00 USD
// Allocate in ratio of [1,1,2]
allocation, err := dollars.Allocate([]int64{1, 1, 2})
if err != nil {
fmt.Println("Error:", err)
return
}
for i, part := range allocation.Parts {
fmt.Printf("Part %d: %v\n", i+1, part)
}
Output: Part 1: $25.00 Part 2: $25.00 Part 3: $50.00
func (Money[T]) Amount ¶
Amount returns the internal amount value in the currency's smallest unit. For example, returns cents for USD or pence for GBP.
func (Money[T]) Cmp ¶
Cmp compares two Money values and returns: -1 if m < other
0 if m == other
+1 if m > other
func (Money[T]) Distribute ¶
func (m Money[T]) Distribute(chunks int64) (Distribution, error)
Distribute splits the money amount into the specified number of chunks Returns a Distribution describing how to split the money
Example ¶
dollars := NewMoney[currency.USD](10000) // 100.00 USD
// Distribute into 3 chunks
dist, err := dollars.Distribute(3)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("Smaller chunks: %d x $%.2f\n",
dist.SmallerCount,
float64(dist.SmallerChunkSize)/100)
fmt.Printf("Larger chunks: %d x $%.2f\n",
dist.LargerCount,
float64(dist.LargerChunkSize)/100)
// Verify total
total := (dist.SmallerChunkSize * dist.SmallerCount) +
(dist.LargerChunkSize * dist.LargerCount)
fmt.Printf("Total: $%.2f\n", float64(total)/100)
Output: Smaller chunks: 2 x $33.33 Larger chunks: 1 x $33.34 Total: $100.00
func (Money[T]) Div ¶
func (m Money[T]) Div(divisor int64, mode RoundingMode) (Money[T], error)
Div divides the Money value by a scalar value using the specified rounding mode. Returns ErrDivisionByZero if divisor is 0. Returns ErrOverflow if the operation would overflow int64. Returns ErrInvalidRoundingMode if the rounding mode is unsupported.
func (Money[T]) Format ¶
Format returns a formatted string representation of the Money value for the specified locale.
func (Money[T]) GreaterThan ¶
GreaterThan returns true if the Money value is greater than the other.
func (Money[T]) GreaterThanOrEqual ¶
GreaterThanOrEqual returns true if the Money value is greater than or equal to the other.
func (Money[T]) IsNegative ¶
IsNegative returns true if the Money amount is less than zero.
func (Money[T]) IsPositive ¶
IsPositive returns true if the Money amount is greater than zero.
func (Money[T]) LessThanOrEqual ¶
LessThanOrEqual returns true if the Money value is less than or equal to the other.
func (Money[T]) MarshalJSON ¶
MarshalJSON implements the json.Marshaler interface Serializes the amount as a string to preserve precision
func (Money[T]) Mul ¶
Mul multiplies the Money value by a scalar value. Returns ErrOverflow if the operation would overflow int64. If scale is 0, sets the amount to 0 and returns nil.
func (Money[T]) MustAdd ¶
MustAdd performs addition of two Money values of the same currency. Panics if the operation would overflow int64.
func (Money[T]) MustMul ¶
MustMul multiplies the Money value by a scalar value. Panics if the operation would overflow int64.
func (Money[T]) MustSub ¶
MustSub performs subtraction of two Money values of the same currency. Panics if the operation would overflow int64.
func (Money[T]) Neg ¶
Neg returns the negated value of the money amount. Returns ErrOverflow if the amount is math.MinInt64.
func (Money[T]) String ¶
String returns a formatted string representation of the Money value using the default locale. This implements the fmt.Stringer interface.
func (Money[T]) Sub ¶
Sub performs subtraction of two Money values of the same currency. Returns ErrOverflow if the operation would overflow int64.
func (*Money[T]) UnmarshalJSON ¶
UnmarshalJSON implements the json.Unmarshaler interface Expects amount as a string to maintain precision
type Ratio ¶
type Ratio[F currency.Currency, T currency.Currency] struct { // Numerator is the top number in the fraction (e.g., 107203 for 1.07203) Numerator int64 // Denominator is the bottom number in the fraction (e.g., 100000 for precise decimal representation) Denominator int64 }
Ratio represents a fraction used for conversion rates
func ParseRatioFloat64 ¶
ParseRatioFloat64 parses a float64 representation of an exchange rate into a Ratio. It formats the float to a string to avoid floating point precision issues.
type RoundingMode ¶
type RoundingMode int
RoundingMode controls how division results are rounded in conversion.
const ( // RoundTruncate rounds toward zero (default behavior). RoundTruncate RoundingMode = iota // RoundHalfUp rounds to nearest, ties away from zero. RoundHalfUp // RoundHalfEven rounds to nearest, ties to even. RoundHalfEven )
