-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Co-authored-by: Julián Toledano <[email protected]> Co-authored-by: Julien Robert <[email protected]>
- Loading branch information
1 parent
96a3016
commit 4a73a1e
Showing
7 changed files
with
209 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package flag | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"strings" | ||
|
||
"google.golang.org/protobuf/reflect/protoreflect" | ||
|
||
basev1beta1 "cosmossdk.io/api/cosmos/base/v1beta1" | ||
"cosmossdk.io/client/v2/internal/coins" | ||
) | ||
|
||
type decCoinType struct{} | ||
|
||
type decCoinValue struct { | ||
value *basev1beta1.DecCoin | ||
} | ||
|
||
func (c decCoinType) NewValue(*context.Context, *Builder) Value { | ||
return &decCoinValue{} | ||
} | ||
|
||
func (c decCoinType) DefaultValue() string { | ||
return "zero" | ||
} | ||
|
||
func (c *decCoinValue) Get(protoreflect.Value) (protoreflect.Value, error) { | ||
if c.value == nil { | ||
return protoreflect.Value{}, nil | ||
} | ||
return protoreflect.ValueOfMessage(c.value.ProtoReflect()), nil | ||
} | ||
|
||
func (c *decCoinValue) String() string { | ||
if c.value == nil { | ||
return "" | ||
} | ||
|
||
return c.value.String() | ||
} | ||
|
||
func (c *decCoinValue) Set(stringValue string) error { | ||
if strings.Contains(stringValue, ",") { | ||
return errors.New("coin flag must be a single coin, specific multiple coins with multiple flags or spaces") | ||
} | ||
|
||
coin, err := coins.ParseDecCoin(stringValue) | ||
if err != nil { | ||
return err | ||
} | ||
c.value = coin | ||
return nil | ||
} | ||
|
||
func (c *decCoinValue) Type() string { | ||
return "cosmos.base.v1beta1.DecCoin" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package coins | ||
|
||
import ( | ||
"errors" | ||
"regexp" | ||
"strings" | ||
|
||
basev1beta1 "cosmossdk.io/api/cosmos/base/v1beta1" | ||
) | ||
|
||
// Amount can be a whole number or a decimal number. Denominations can be 3 ~ 128 | ||
// characters long and support letters, followed by either a letter, a number or | ||
// a separator ('/', ':', '.', '_' or '-'). | ||
var coinRegex = regexp.MustCompile(`^(\d+(\.\d+)?)([a-zA-Z][a-zA-Z0-9\/\:\._\-]{2,127})$`) | ||
|
||
// ParseCoin parses a coin from a string. The string must be in the format | ||
// <amount><denom>, where <amount> is a number and <denom> is a valid denom. | ||
func ParseCoin(input string) (*basev1beta1.Coin, error) { | ||
amount, denom, err := parseCoin(input) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &basev1beta1.Coin{ | ||
Amount: amount, | ||
Denom: denom, | ||
}, nil | ||
} | ||
|
||
// ParseDecCoin parses a decCoin from a string. The string must be in the format | ||
// <amount><denom>, where <amount> is a number and <denom> is a valid denom. | ||
func ParseDecCoin(input string) (*basev1beta1.DecCoin, error) { | ||
amount, denom, err := parseCoin(input) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &basev1beta1.DecCoin{ | ||
Amount: amount, | ||
Denom: denom, | ||
}, nil | ||
} | ||
|
||
// parseCoin parses a coin string into its amount and denom components. | ||
// The input string must be in the format <amount><denom>. | ||
// It returns the amount string, denom string, and any error encountered. | ||
// Returns an error if the input is empty or doesn't match the expected format. | ||
func parseCoin(input string) (amount, denom string, err error) { | ||
input = strings.TrimSpace(input) | ||
|
||
if input == "" { | ||
return "", "", errors.New("empty input when parsing coin") | ||
} | ||
|
||
matches := coinRegex.FindStringSubmatch(input) | ||
|
||
if len(matches) == 0 { | ||
return "", "", errors.New("invalid input format") | ||
} | ||
|
||
return matches[1], matches[3], nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package coins | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func Test_parseCoin(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
input string | ||
amount string | ||
denom string | ||
err string | ||
}{ | ||
{ | ||
name: "ok", | ||
input: "1000stake", | ||
amount: "1000", | ||
denom: "stake", | ||
}, | ||
{ | ||
name: "empty", | ||
input: "", | ||
err: "empty input when parsing coin", | ||
}, | ||
{ | ||
name: "empty denom", | ||
input: "1000", | ||
err: "invalid input format", | ||
}, | ||
{ | ||
name: "empty amount", | ||
input: "stake", | ||
err: "invalid input format", | ||
}, | ||
{ | ||
name: "<denom><amount> format", | ||
input: "stake1000", | ||
err: "invalid input format", | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
amount, denom, err := parseCoin(tt.input) | ||
if tt.err != "" { | ||
require.Error(t, err) | ||
require.Contains(t, err.Error(), tt.err) | ||
} else { | ||
require.NoError(t, err) | ||
require.Equal(t, tt.amount, amount) | ||
require.Equal(t, tt.denom, denom) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestParseCoin(t *testing.T) { | ||
encodedCoin := "1000000000foo" | ||
coin, err := ParseCoin(encodedCoin) | ||
require.NoError(t, err) | ||
require.Equal(t, "1000000000", coin.Amount) | ||
require.Equal(t, "foo", coin.Denom) | ||
} | ||
|
||
func TestParseDecCoin(t *testing.T) { | ||
encodedCoin := "1000000000foo" | ||
coin, err := ParseDecCoin(encodedCoin) | ||
require.NoError(t, err) | ||
require.Equal(t, "1000000000", coin.Amount) | ||
require.Equal(t, "foo", coin.Denom) | ||
} |