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
- 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]) 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]) Distribute(chunks int64) (Distribution, error)
- func (m Money[T]) Format(locale locale.Locale) string
- func (m Money[T]) MarshalJSON() ([]byte, error)
- func (m Money[T]) Mul(scale int64) (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") // 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") )
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]) 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]) 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]) Format ¶
Format returns a formatted string representation of the Money value for the specified locale.
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]) 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
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 )
