Skip to content

Commit

Permalink
Merge pull request #3 from bnkamalesh/fmt-stringer-interface
Browse files Browse the repository at this point in the history
Fmt stringer interface
  • Loading branch information
bnkamalesh authored Nov 13, 2021
2 parents 4a86204 + b8d3a76 commit 98a483f
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 33 deletions.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@
[![awesome-go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go#financial)


## Currency v1.0.0
## Currency v2.0.0

Currency package helps you do currency computations accurately, by avoiding *_peddling_*.
The `Currency` struct holds all the data required to define a currency.
Currency package helps you do currency computations accurately. `Currency` struct holds all the data required to define a currency.

```
type Currency struct {
Expand All @@ -29,6 +28,10 @@ type Currency struct {
// FUShare represents the no.of fractional/sub units that make up 1 main unit. e.g. ₹1 = 100 paise
// Number of fractional units that make up 1 unit of the main value
FUShare uint
// PrefixSymbol if true will prefix the symbol when stringified
PrefixSymbol bool
// SuffixSymbol if true will suffix the symbol when stringified
SuffixSymbol bool
}
```

Expand Down Expand Up @@ -81,7 +84,7 @@ e.g. ₹1/- (INR 1) is to be divided by 3. There are 2 options of dividing this

### Multiple currency representations

1. `c1.String(prefixSymbol bool)`, returns a string representation of the currency value. Returns string prefixed by its respective symbol if `prefixSymbol` is true
1. `c1.String()`, returns a string representation of the currency value
2. `c1.Float64()`, returns a float64 representation of the currency value

## Benchmarks
Expand Down
30 changes: 22 additions & 8 deletions currency.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ type Currency struct {
fuDigits int `json:"fuDigits,omitempty"`
// magnitude is the fraction which sets the magnitude required for the rounding function
magnitude float64 `json:"magnitude,omitempty"`
// PrefixSymbol if true will add the symbol as a prefix to the string representation of currency. e.g. ₹1.5
PrefixSymbol bool `json:"alwaysAddPrefix,omitempty"`
// SuffixSymbol if true will add the symbol as a suffix to the string representation of currency. e.g. 1.5₹
SuffixSymbol bool `json:"alwaysAddSuffix,omitempty"`
}

// New returns a new instance of currency.
Expand Down Expand Up @@ -190,8 +194,7 @@ func (c *Currency) Float64() float64 {
return float64(c.Main) + (float64(frac) / float64(c.FUShare))
}

// String returns the currency represented as string.
func (c *Currency) String(prefixSymbol bool) string {
func (c *Currency) StringWithoutSymbols() string {
frc := c.Fractional
if c.Fractional < 0 {
frc = -frc
Expand All @@ -209,24 +212,35 @@ func (c *Currency) String(prefixSymbol bool) string {
if c.Fractional < 0 {
str = "-" + str
}
return str
}

if prefixSymbol {
// String returns the currency represented as string.
func (c *Currency) String() string {
str := c.StringWithoutSymbols()

if c.PrefixSymbol {
if c.Fractional < 0 || c.Main < 0 {
str = strings.Replace(str, "-", "-"+c.Symbol, 1)
} else {
str = c.Symbol + str
}
}

if c.SuffixSymbol {
str = str + c.Symbol
}

return str
}

func (c *Currency) Format(s fmt.State, verb rune) {
switch verb {
// 's' verb would produce the full currency string along with its symbol. equivalent to c.String(true)
case 's':
// 's' verb would produce the full currency string along with its symbol. equivalent to c.String()
// 'v' - only once you add this does the fmt.Stringer seem to work, otherwise it's always printing blank
case 's', 'v':
{
_, _ = io.WriteString(s, c.String(true))
_, _ = io.WriteString(s, c.String())
}
// 'd' verb would produce the main integer part of the currency, without the symbol
case 'd':
Expand All @@ -240,10 +254,10 @@ func (c *Currency) Format(s fmt.State, verb rune) {
main := strconv.Itoa(c.Fractional)
_, _ = io.WriteString(s, main)
}
// 'f' verb would produce the full currency string without its symbol. equivalent to c.String(false)
// 'f' verb would produce the full currency string without its symbol. equivalent to c.StringWithoutSymbols()
case 'f':
{
_, _ = io.WriteString(s, c.String(false))
_, _ = io.WriteString(s, c.StringWithoutSymbols())
}
// 'y' verb would produce the currency symbol alone
case 'y':
Expand Down
38 changes: 32 additions & 6 deletions currency_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ func TestNew(t *testing.T) {
t.Fail()
}

str := cur.String(true)
cur.PrefixSymbol = true
str := cur.String()
if str != nT.out.str {
t.Log("Expected:", nT.out.str, "got:", str)
t.Fail()
Expand Down Expand Up @@ -137,7 +138,8 @@ func TestNewFractional(t *testing.T) {
t.Fail()
}

str := cur.String(true)
cur.PrefixSymbol = true
str := cur.String()
if str != nT.out.str {
t.Log("Expected:", nT.out.str, "got:", str)
t.Fail()
Expand Down Expand Up @@ -179,7 +181,8 @@ func TestParseStr(t *testing.T) {
t.Fail()
}

str := cur.String(true)
cur.PrefixSymbol = true
str := cur.String()
if str != nT.out.str {
t.Log("Expected:", nT.out.str, "got:", str)
t.Fail()
Expand Down Expand Up @@ -221,7 +224,8 @@ func TestParseFloat64(t *testing.T) {
t.Fail()
}

str := cur.String(true)
cur.PrefixSymbol = true
str := cur.String()
if str != nT.out.str {
t.Log("Expected:", nT.out.str, "got:", str)
t.Fail()
Expand All @@ -245,11 +249,29 @@ func TestFormat(t *testing.T) {
list := []struct {
Verb string
Expected string
Prefix bool
Suffix bool
}{
{
Verb: "s",
Expected: "12.75",
},
{
Verb: "s",
Prefix: true,
Expected: "₹12.75",
},
{
Verb: "s",
Suffix: true,
Expected: "12.75₹",
},
{
Verb: "s",
Suffix: true,
Prefix: true,
Expected: "₹12.75₹",
},
{
Verb: "d",
Expected: "12",
Expand All @@ -268,6 +290,9 @@ func TestFormat(t *testing.T) {
},
}
for _, l := range list {
c.PrefixSymbol = l.Prefix
c.SuffixSymbol = l.Suffix

formatstr := "%" + l.Verb
str := fmt.Sprintf(formatstr, c)
if str != l.Expected {
Expand Down Expand Up @@ -302,15 +327,16 @@ func BenchmarkParseString(t *testing.B) {

func BenchmarkString(t *testing.B) {
cur1, _ := New(10, 5, "INR", "₹", "paise", 100)
cur1.PrefixSymbol = true
for i := 0; i < t.N; i++ {
cur1.String(true)
cur1.String()
}
}

func BenchmarkStringNoPrefix(t *testing.B) {
cur1, _ := New(10, 5, "INR", "₹", "paise", 100)
for i := 0; i < t.N; i++ {
cur1.String(false)
cur1.StringWithoutSymbols()
}
}

Expand Down
45 changes: 30 additions & 15 deletions operations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ func TestUpdateWithFractional(t *testing.T) {
t.Fail()
}

s := cur.String(true)
cur.PrefixSymbol = true
s := cur.String()
if s != "₹10.05" {
t.Log("Expected ₹10.05, got:", s)
t.Fail()
Expand Down Expand Up @@ -59,7 +60,8 @@ func TestAdd(t *testing.T) {
t.Fail()
}

str := cur1.String(true)
cur1.PrefixSymbol = true
str := cur1.String()
if str != "₹21.98" {
t.Log("Expected ₹21.98, got:", str)
t.Fail()
Expand Down Expand Up @@ -98,7 +100,8 @@ func TestAdd2(t *testing.T) {
t.Fail()
}

str := cur1.String(true)
cur1.PrefixSymbol = true
str := cur1.String()
if str != "₹0.00" {
t.Log("Expected ₹0.00, got:", str)
t.Fail()
Expand Down Expand Up @@ -136,7 +139,8 @@ func TestAdd3(t *testing.T) {
t.Fail()
}

str := cur1.String(true)
cur1.PrefixSymbol = true
str := cur1.String()
if str != "-₹20.50" {
t.Log("Expected -₹20.50, got:", str)
t.Fail()
Expand Down Expand Up @@ -168,7 +172,8 @@ func TestAddInt(t *testing.T) {
t.Fail()
}

str := cur.String(true)
cur.PrefixSymbol = true
str := cur.String()
if str != "₹21.09" {
t.Log("Expected:", "₹21.09", "got:", str)
t.Fail()
Expand Down Expand Up @@ -207,7 +212,8 @@ func TestSubtract(t *testing.T) {
t.Fail()
}

str := cur1.String(true)
cur1.PrefixSymbol = true
str := cur1.String()
if str != "-₹1.00" {
t.Log("Expected -₹1.00, got:", str)
t.Fail()
Expand Down Expand Up @@ -246,7 +252,8 @@ func TestSub2(t *testing.T) {
t.Fail()
}

str := cur1.String(true)
cur1.PrefixSymbol = true
str := cur1.String()
if str != "₹4.70" {
t.Log("Expected ₹4.70, got:", str)
t.Fail()
Expand Down Expand Up @@ -278,7 +285,8 @@ func TestSubtractInt(t *testing.T) {
t.Fail()
}

str := cur.String(true)
cur.PrefixSymbol = true
str := cur.String()
if str != "-₹0.09" {
t.Log("Expected:", "-₹0.09", "got:", str)
t.Fail()
Expand Down Expand Up @@ -310,7 +318,8 @@ func TestMultiply(t *testing.T) {
t.Fail()
}

str := cur1.String(true)
cur1.PrefixSymbol = true
str := cur1.String()
if str != "₹52.50" {
t.Log("Expected ₹52.50, got:", str)
t.Fail()
Expand Down Expand Up @@ -342,7 +351,8 @@ func TestMultiplyFloat64(t *testing.T) {
t.Fail()
}

str := cur1.String(true)
cur1.PrefixSymbol = true
str := cur1.String()
if str != "₹11.03" {
t.Log("Expected ₹11.03, got:", str)
t.Fail()
Expand Down Expand Up @@ -374,7 +384,8 @@ func TestPercent(t *testing.T) {
t.Fail()
}

str := cur2.String(true)
cur2.PrefixSymbol = true
str := cur2.String()
if str != "₹0.55" {
t.Log("Expected ₹0.55, got:", str)
t.Fail()
Expand All @@ -400,7 +411,8 @@ func TestDivideRetain(t *testing.T) {
t.Fail()
}

for _, split := range splits {
for idx := range splits {
split := splits[idx]
if split.Main != 0 {
t.Log("Expected", 0, "got:", split.Main)
t.Fail()
Expand All @@ -411,7 +423,8 @@ func TestDivideRetain(t *testing.T) {
t.Fail()
}

str := split.String(true)
split.PrefixSymbol = true
str := split.String()
if str != "₹0.33" {
t.Log("Expected:", "₹0.33", "got:", str)
t.Fail()
Expand Down Expand Up @@ -443,7 +456,8 @@ func TestDivideNoRetain(t *testing.T) {
t.Fail()
}

for _, split := range splits {
for idx := range splits {
split := splits[idx]
if split.Main != 0 {
t.Log("Expected", 0, "got:", split.Main)
t.Fail()
Expand All @@ -454,7 +468,8 @@ func TestDivideNoRetain(t *testing.T) {
t.Fail()
}

str := split.String(true)
split.PrefixSymbol = true
str := split.String()
if str != "₹0.33" && str != "₹0.34" {
t.Log("Expected:", "₹0.33 or ₹0.34", "got:", str)
t.Fail()
Expand Down

0 comments on commit 98a483f

Please sign in to comment.