generated from dogmatiq/template-go
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmarshalproto.go
115 lines (93 loc) · 3.2 KB
/
marshalproto.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package dosh
import (
"errors"
"fmt"
"github.com/dogmatiq/dosh/internal/currency"
"github.com/shopspring/decimal"
"google.golang.org/genproto/googleapis/type/money"
)
// nanosPerUnit is the number of "nano units" in each unit.
var nanosPerUnit = decimal.NewFromInt(1_000_000_000)
// FromProto unmarshals an amount from its protocol buffers representation, or
// panics if unable to do so.
func FromProto(m *money.Money) Amount {
var a Amount
if err := a.UnmarshalProto(m); err != nil {
panic(err)
}
return a
}
// ToProto marshals an amount to its protocol buffers representation, or panics
// if unable to do so.
func ToProto(a Amount) *money.Money {
pb, err := a.MarshalProto()
if err != nil {
panic(err)
}
return pb
}
// MarshalProto mashals an amount to its protocol buffers representation.
func (a Amount) MarshalProto() (*money.Money, error) {
pb, err := a.marshalProto()
if err != nil {
return nil, fmt.Errorf("cannot marshal amount to protocol buffers representation: %w", err)
}
return pb, nil
}
// UnmarshalProto unmarshals an amount from its protocol buffers representation.
//
// NOTE: For consistency with other UnmarshalXXX() methods, this method mutates
// the internals of a, violating Amount's immutability guarantee.
func (a *Amount) UnmarshalProto(pb *money.Money) error {
if err := a.unmarshalProto(pb); err != nil {
return fmt.Errorf("cannot unmarshal amount from protocol buffers representation: %w", err)
}
return nil
}
// marshalProto mashals an amount to its protocol buffers representation,
// without providing any protocol-buffer-specific error information, allowing it
// to be used by both MarshalJSON() and MarshalProto().
func (a Amount) marshalProto() (*money.Money, error) {
if !a.mag.BigInt().IsInt64() {
return nil, errors.New("magnitude's integer component overflows int64")
}
// Isolate the fractional part and multiply it by nanosPerUnit to work out
// how many nano units we have.
nanos := a.mag.
Mod(unit).
Mul(nanosPerUnit)
// If after doing so nanos still contains a fractional component then
// the decimal has more decimal places than can be represented using
// nano units.
if !nanos.Mod(unit).Equal(decimal.Zero) {
return nil, errors.New("magnitude's fractional component has too many decimal places")
}
return &money.Money{
CurrencyCode: a.CurrencyCode(),
Units: a.mag.IntPart(),
Nanos: int32(nanos.IntPart()),
}, nil
}
// unmarshalProto unmashals an amount from its protocol buffers representation,
// without providing any protocol-buffer-specific error information, allowing it
// to be used by both UnmarshalJSON() and UnmarshalProto().
func (a *Amount) unmarshalProto(pb *money.Money) error {
c := pb.GetCurrencyCode()
if err := currency.ValidateCode(c); err != nil {
return err
}
units := pb.GetUnits()
nanos := int64(pb.GetNanos())
if (units > 0 && nanos < 0) || (units < 0 && nanos > 0) {
return errors.New("units and nanos components must have the same sign")
}
// normalize any overflowing nanos component into units.
units += int64(nanos) / nanosPerUnit.IntPart()
nanos %= nanosPerUnit.IntPart()
m := decimal.NewFromInt(units).Add(
decimal.NewFromInt(nanos).Div(nanosPerUnit),
)
a.cur = c
a.mag = m
return nil
}