|
| 1 | +package money |
| 2 | + |
| 3 | +import "math" |
| 4 | + |
| 5 | +// Money represents a monetary value stored in minor units |
| 6 | +// (for example cents) to avoid floating point errors. |
| 7 | +type Money struct { |
| 8 | + amount uint64 |
| 9 | +} |
| 10 | + |
| 11 | +// New creates a new Money value from minor units. |
| 12 | +func New(minor uint64) Money { |
| 13 | + return Money{amount: minor} |
| 14 | +} |
| 15 | + |
| 16 | +// Minor returns the value in minor units. |
| 17 | +func (m Money) Minor() uint64 { |
| 18 | + return m.amount |
| 19 | +} |
| 20 | + |
| 21 | +// Add returns a new Money with the sum of two values. |
| 22 | +func (m Money) Add(other Money) Money { |
| 23 | + return Money{ |
| 24 | + amount: m.amount + other.amount, |
| 25 | + } |
| 26 | +} |
| 27 | + |
| 28 | +// Sub subtracts the other value. |
| 29 | +// It panics if the result would be negative. |
| 30 | +func (m Money) Sub(other Money) Money { |
| 31 | + if other.amount > m.amount { |
| 32 | + panic("money: subtraction would result in negative value") |
| 33 | + } |
| 34 | + return Money{ |
| 35 | + amount: m.amount - other.amount, |
| 36 | + } |
| 37 | +} |
| 38 | + |
| 39 | +// Mul multiplies the amount by a floating-point factor. |
| 40 | +// The result is always rounded down to the nearest minor unit. |
| 41 | +func (m Money) Mul(factor float64) Money { |
| 42 | + if factor < 0 { |
| 43 | + panic("money: negative multiplier") |
| 44 | + } |
| 45 | + return Money{ |
| 46 | + amount: uint64(math.Floor(float64(m.amount) * factor)), |
| 47 | + } |
| 48 | +} |
| 49 | + |
| 50 | +// Split divides the money amount into n parts. |
| 51 | +// The split is exact: the sum of all parts equals the original amount. |
| 52 | +// Any remainder is distributed by adding 1 minor unit to the first parts. |
| 53 | +func (m Money) Split(n int) []Money { |
| 54 | + if n <= 0 { |
| 55 | + panic("money: split count must be positive") |
| 56 | + } |
| 57 | + |
| 58 | + base := m.amount / uint64(n) |
| 59 | + rem := m.amount % uint64(n) |
| 60 | + |
| 61 | + out := make([]Money, n) |
| 62 | + for i := range n { |
| 63 | + amt := base |
| 64 | + if uint64(i) < rem { |
| 65 | + amt++ |
| 66 | + } |
| 67 | + out[i] = Money{amount: amt} |
| 68 | + } |
| 69 | + |
| 70 | + return out |
| 71 | +} |
| 72 | + |
| 73 | +// IsZero checks if the money value is zero. |
| 74 | +func (m Money) IsZero() bool { |
| 75 | + return m.amount == 0 |
| 76 | +} |
| 77 | + |
| 78 | +// CurrencyAdapter defines how to convert minor units |
| 79 | +// into major units for a specific currency. |
| 80 | +type CurrencyAdapter interface { |
| 81 | + MinorToMajor(minor uint64) float64 |
| 82 | +} |
| 83 | + |
| 84 | +// ToMajorUnits converts the money value to major units |
| 85 | +// using the provided currency adapter. |
| 86 | +func (m Money) ToMajorUnits(adapter CurrencyAdapter) float64 { |
| 87 | + return adapter.MinorToMajor(m.amount) |
| 88 | +} |
0 commit comments