From faac776ca466d7aa57e5105470dc822334f13eab Mon Sep 17 00:00:00 2001 From: Aaron Lindsay Date: Mon, 17 Apr 2017 20:11:53 -0400 Subject: [PATCH] Add Currency type --- types.go | 52 +++++++++++++++++++++++++++++++++++++++ types_test.go | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) diff --git a/types.go b/types.go index cbb27b0..ff5fda2 100644 --- a/types.go +++ b/types.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "github.com/aclindsa/go/src/encoding/xml" + "golang.org/x/text/currency" "math/big" "regexp" "strconv" @@ -321,3 +322,54 @@ func RandomUID() (*UID, error) { uid := UID(fmt.Sprintf("%08x-%04x-%04x-%04x-%012x", uidbytes[:4], uidbytes[4:6], uidbytes[6:8], uidbytes[8:10], uidbytes[10:])) return &uid, nil } + +// CurrSymbol represents an ISO-4217 currency +type CurrSymbol struct { + currency.Unit +} + +// UnmarshalXML handles unmarshalling a CurrSymbol from an SGML/XML string. +// Leading and trailing whitespace is ignored. +func (c *CurrSymbol) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + var value string + + err := d.DecodeElement(&value, &start) + if err != nil { + return err + } + + value = strings.TrimSpace(value) + + unit, err := currency.ParseISO(value) + if err != nil { + errors.New("Error parsing CurrSymbol:" + err.Error()) + } + c.Unit = unit + return nil +} + +// MarshalXML marshals a CurrSymbol to SGML/XML +func (c *CurrSymbol) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + return e.EncodeElement(c.String(), start) +} + +// Equal returns true if the two Currencies are the same +func (c CurrSymbol) Equal(o CurrSymbol) bool { + return c.String() == o.String() +} + +// Valid returns true, nil if the CurrSymbol is valid. +func (c CurrSymbol) Valid() (bool, error) { + if c.String() == "XXX" { + return false, fmt.Errorf("Invalid CurrSymbol: %s", c.Unit) + } + return true, nil +} + +func NewCurrSymbol(s string) (*CurrSymbol, error) { + unit, err := currency.ParseISO(s) + if err != nil { + return nil, errors.New("Error parsing string to create new CurrSymbol:" + err.Error()) + } + return &CurrSymbol{unit}, nil +} diff --git a/types_test.go b/types_test.go index d07803e..0497223 100644 --- a/types_test.go +++ b/types_test.go @@ -373,3 +373,71 @@ func TestRandomUID(t *testing.T) { t.Fatalf("UID generated with RandomUID() doesn't match recommended format: %s\n", err) } } + +func TestMarshalCurrSymbol(t *testing.T) { + c, _ := ofxgo.NewCurrSymbol("USD") + marshalHelper(t, "USD", &c) +} + +func TestUnmarshalCurrSymbol(t *testing.T) { + var overwritten ofxgo.CurrSymbol + c, _ := ofxgo.NewCurrSymbol("USD") + unmarshalHelper(t, "USD", c, &overwritten) + // Make sure stray newlines are handled properly + c, _ = ofxgo.NewCurrSymbol("EUR") + unmarshalHelper(t, "EUR\n", c, &overwritten) + unmarshalHelper(t, "EUR\n\t", c, &overwritten) +} + +func TestCurrSymbolEqual(t *testing.T) { + usd1, _ := ofxgo.NewCurrSymbol("USD") + usd2, _ := ofxgo.NewCurrSymbol("USD") + if !usd1.Equal(*usd2) { + t.Fatalf("Two \"USD\" CurrSymbols returned !Equal()\n") + } + xxx, _ := ofxgo.NewCurrSymbol("XXX") + if usd1.Equal(*xxx) { + t.Fatalf("\"USD\" and \"XXX\" CurrSymbols returned Equal()\n") + } +} + +func TestCurrSymbolValid(t *testing.T) { + var initial ofxgo.CurrSymbol + ok, err := initial.Valid() + if ok || err == nil { + t.Fatalf("CurrSymbol unexpectedly returned Valid() for initial value\n") + } + + ars, _ := ofxgo.NewCurrSymbol("ARS") + ok, err = ars.Valid() + if !ok || err != nil { + t.Fatalf("CurrSymbol unexpectedly returned !Valid() for \"ARS\": %s\n", err.Error()) + } + + xxx, _ := ofxgo.NewCurrSymbol("XXX") + ok, err = xxx.Valid() + if ok || err == nil { + t.Fatalf("CurrSymbol unexpectedly returned Valid() for \"XXX\"\n") + } +} + +func TestNewCurrSymbol(t *testing.T) { + curr, err := ofxgo.NewCurrSymbol("GBP") + if err != nil { + t.Fatalf("Unexpected error calling NewCurrSymbol: %s\n", err) + } + if curr.String() != "GBP" { + t.Fatalf("Created CurrSymbol doesn't print \"GBP\" as string representation\n") + } + curr, err = ofxgo.NewCurrSymbol("AFN") + if err != nil { + t.Fatalf("Unexpected error calling NewCurrSymbol: %s\n", err) + } + if curr.String() != "AFN" { + t.Fatalf("Created CurrSymbol doesn't print \"AFN\" as string representation\n") + } + curr, err = ofxgo.NewCurrSymbol("BLAH") + if err == nil { + t.Fatalf("NewCurrSymbol didn't error on invalid currency identifier\n") + } +}