cosmos-sdk/types/coin.go

220 lines
4.2 KiB
Go
Raw Normal View History

2016-04-01 15:19:07 -07:00
package types
import (
"fmt"
2017-03-28 13:32:55 -07:00
"regexp"
"sort"
2017-03-28 13:32:55 -07:00
"strconv"
2016-04-01 15:19:07 -07:00
"strings"
"github.com/pkg/errors"
2016-04-01 15:19:07 -07:00
)
type Coin struct {
Denom string `json:"denom"`
Amount int64 `json:"amount"`
}
func (coin Coin) String() string {
2017-03-28 13:32:55 -07:00
return fmt.Sprintf("%v%v", coin.Amount, coin.Denom)
}
//regex codes for extracting coins from string
2017-05-24 08:36:24 -07:00
var reDenom = regexp.MustCompile("")
2017-03-28 13:32:55 -07:00
var reAmt = regexp.MustCompile("(\\d+)")
2017-05-24 08:36:24 -07:00
var reCoin = regexp.MustCompile("^([[:digit:]]+)[[:space:]]*([[:alpha:]]+)$")
2017-03-28 13:32:55 -07:00
func ParseCoin(str string) (Coin, error) {
var coin Coin
2017-05-24 08:36:24 -07:00
matches := reCoin.FindStringSubmatch(strings.TrimSpace(str))
if matches == nil {
return coin, errors.Errorf("%s is invalid coin definition", str)
2017-03-28 13:32:55 -07:00
}
2017-05-24 08:36:24 -07:00
// parse the amount (should always parse properly)
amt, err := strconv.Atoi(matches[1])
2017-05-24 08:15:42 -07:00
if err != nil {
return coin, err
}
2017-05-24 08:36:24 -07:00
coin = Coin{matches[2], int64(amt)}
2017-03-28 13:32:55 -07:00
return coin, nil
2016-04-01 15:19:07 -07:00
}
//----------------------------------------
type Coins []Coin
func (coins Coins) String() string {
if len(coins) == 0 {
return ""
}
out := ""
for _, coin := range coins {
2017-03-28 13:32:55 -07:00
out += fmt.Sprintf("%v,", coin.String())
}
2017-04-13 18:33:39 -07:00
return out[:len(out)-1]
}
2017-03-28 13:32:55 -07:00
func ParseCoins(str string) (Coins, error) {
2017-05-24 08:15:42 -07:00
// empty string is empty list...
if len(str) == 0 {
return nil, nil
}
2017-03-28 13:32:55 -07:00
split := strings.Split(str, ",")
var coins Coins
2017-03-28 13:32:55 -07:00
for _, el := range split {
2017-05-24 08:15:42 -07:00
coin, err := ParseCoin(el)
if err != nil {
return coins, err
2017-03-28 13:32:55 -07:00
}
2017-05-24 08:15:42 -07:00
coins = append(coins, coin)
2017-03-28 13:32:55 -07:00
}
// ensure they are in proper order, to avoid random failures later
coins.Sort()
if !coins.IsValid() {
return nil, errors.Errorf("ParseCoins invalid: %#v", coins)
}
2017-03-28 13:32:55 -07:00
return coins, nil
}
2016-04-01 15:19:07 -07:00
// Must be sorted, and not have 0 amounts
func (coins Coins) IsValid() bool {
switch len(coins) {
case 0:
return true
case 1:
return coins[0].Amount != 0
default:
lowDenom := coins[0].Denom
for _, coin := range coins[1:] {
if coin.Denom <= lowDenom {
return false
}
if coin.Amount == 0 {
return false
}
2017-01-31 03:24:49 -08:00
// we compare each coin against the last denom
lowDenom = coin.Denom
2016-04-01 15:19:07 -07:00
}
return true
}
}
2017-02-24 14:12:05 -08:00
// TODO: handle empty coins!
// Currently appends an empty coin ...
2016-04-01 15:19:07 -07:00
func (coinsA Coins) Plus(coinsB Coins) Coins {
sum := []Coin{}
indexA, indexB := 0, 0
lenA, lenB := len(coinsA), len(coinsB)
for {
if indexA == lenA {
if indexB == lenB {
return sum
} else {
return append(sum, coinsB[indexB:]...)
}
} else if indexB == lenB {
return append(sum, coinsA[indexA:]...)
}
coinA, coinB := coinsA[indexA], coinsB[indexB]
switch strings.Compare(coinA.Denom, coinB.Denom) {
case -1:
sum = append(sum, coinA)
indexA += 1
case 0:
if coinA.Amount+coinB.Amount == 0 {
// ignore 0 sum coin type
} else {
sum = append(sum, Coin{
Denom: coinA.Denom,
Amount: coinA.Amount + coinB.Amount,
})
}
indexA += 1
indexB += 1
case 1:
sum = append(sum, coinB)
indexB += 1
}
}
return sum
}
func (coins Coins) Negative() Coins {
res := make([]Coin, 0, len(coins))
for _, coin := range coins {
res = append(res, Coin{
Denom: coin.Denom,
Amount: -coin.Amount,
})
}
return res
}
func (coinsA Coins) Minus(coinsB Coins) Coins {
return coinsA.Plus(coinsB.Negative())
}
func (coinsA Coins) IsGTE(coinsB Coins) bool {
diff := coinsA.Minus(coinsB)
if len(diff) == 0 {
return true
}
return diff.IsNonnegative()
2016-04-01 15:19:07 -07:00
}
func (coins Coins) IsZero() bool {
return len(coins) == 0
}
func (coinsA Coins) IsEqual(coinsB Coins) bool {
if len(coinsA) != len(coinsB) {
return false
}
for i := 0; i < len(coinsA); i++ {
if coinsA[i] != coinsB[i] {
return false
}
}
return true
}
func (coins Coins) IsPositive() bool {
if len(coins) == 0 {
return false
}
for _, coinAmount := range coins {
if coinAmount.Amount <= 0 {
return false
}
}
return true
}
func (coins Coins) IsNonnegative() bool {
if len(coins) == 0 {
return true
}
for _, coinAmount := range coins {
if coinAmount.Amount < 0 {
return false
}
}
return true
}
/*** Implement Sort interface ***/
func (c Coins) Len() int { return len(c) }
func (c Coins) Less(i, j int) bool { return c[i].Denom < c[j].Denom }
func (c Coins) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c Coins) Sort() { sort.Sort(c) }