summaryrefslogtreecommitdiff
path: root/src/checkout/money/money.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/checkout/money/money.go')
-rw-r--r--src/checkout/money/money.go120
1 files changed, 120 insertions, 0 deletions
diff --git a/src/checkout/money/money.go b/src/checkout/money/money.go
new file mode 100644
index 0000000..ba21cd8
--- /dev/null
+++ b/src/checkout/money/money.go
@@ -0,0 +1,120 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+package money
+
+import (
+ "errors"
+
+ pb "github.com/open-telemetry/opentelemetry-demo/src/checkout/genproto/oteldemo"
+)
+
+const (
+ nanosMin = -999999999
+ nanosMax = +999999999
+ nanosMod = 1000000000
+)
+
+var (
+ ErrInvalidValue = errors.New("one of the specified money values is invalid")
+ ErrMismatchingCurrency = errors.New("mismatching currency codes")
+)
+
+// IsValid checks if specified value has a valid units/nanos signs and ranges.
+func IsValid(m *pb.Money) bool {
+ return signMatches(m) && validNanos(m.GetNanos())
+}
+
+func signMatches(m *pb.Money) bool {
+ return m.GetNanos() == 0 || m.GetUnits() == 0 || (m.GetNanos() < 0) == (m.GetUnits() < 0)
+}
+
+func validNanos(nanos int32) bool { return nanosMin <= nanos && nanos <= nanosMax }
+
+// IsZero returns true if the specified money value is equal to zero.
+func IsZero(m *pb.Money) bool { return m.GetUnits() == 0 && m.GetNanos() == 0 }
+
+// IsPositive returns true if the specified money value is valid and is
+// positive.
+func IsPositive(m *pb.Money) bool {
+ return IsValid(m) && m.GetUnits() > 0 || (m.GetUnits() == 0 && m.GetNanos() > 0)
+}
+
+// IsNegative returns true if the specified money value is valid and is
+// negative.
+func IsNegative(m *pb.Money) bool {
+ return IsValid(m) && m.GetUnits() < 0 || (m.GetUnits() == 0 && m.GetNanos() < 0)
+}
+
+// AreSameCurrency returns true if values l and r have a currency code and
+// they are the same values.
+func AreSameCurrency(l, r *pb.Money) bool {
+ return l.GetCurrencyCode() == r.GetCurrencyCode() && l.GetCurrencyCode() != ""
+}
+
+// AreEquals returns true if values l and r are the equal, including the
+// currency. This does not check validity of the provided values.
+func AreEquals(l, r *pb.Money) bool {
+ return l.GetCurrencyCode() == r.GetCurrencyCode() &&
+ l.GetUnits() == r.GetUnits() && l.GetNanos() == r.GetNanos()
+}
+
+// Negate returns the same amount with the sign negated.
+func Negate(m *pb.Money) *pb.Money {
+ return &pb.Money{
+ Units: -m.GetUnits(),
+ Nanos: -m.GetNanos(),
+ CurrencyCode: m.GetCurrencyCode()}
+}
+
+// Must panics if the given error is not nil. This can be used with other
+// functions like: "m := Must(Sum(a,b))".
+func Must(v *pb.Money, err error) *pb.Money {
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
+// Sum adds two values. Returns an error if one of the values are invalid or
+// currency codes are not matching (unless currency code is unspecified for
+// both).
+func Sum(l, r *pb.Money) (*pb.Money, error) {
+ if !IsValid(l) || !IsValid(r) {
+ return &pb.Money{}, ErrInvalidValue
+ } else if l.GetCurrencyCode() != r.GetCurrencyCode() {
+ return &pb.Money{}, ErrMismatchingCurrency
+ }
+ units := l.GetUnits() + r.GetUnits()
+ nanos := l.GetNanos() + r.GetNanos()
+
+ if (units == 0 && nanos == 0) || (units > 0 && nanos >= 0) || (units < 0 && nanos <= 0) {
+ // same sign <units, nanos>
+ units += int64(nanos / nanosMod)
+ nanos = nanos % nanosMod
+ } else {
+ // different sign. nanos guaranteed to not to go over the limit
+ if units > 0 {
+ units--
+ nanos += nanosMod
+ } else {
+ units++
+ nanos -= nanosMod
+ }
+ }
+
+ return &pb.Money{
+ Units: units,
+ Nanos: nanos,
+ CurrencyCode: l.GetCurrencyCode()}, nil
+}
+
+// MultiplySlow is a slow multiplication operation done through adding the value
+// to itself n-1 times.
+func MultiplySlow(m *pb.Money, n uint32) *pb.Money {
+ out := m
+ for n > 1 {
+ out = Must(Sum(out, m))
+ n--
+ }
+ return out
+}