Skip to content

Commit f511643

Browse files
authored
chore: use cobra helpers for cmd args (#717)
* chore: use cobra helpers for cmd args * fix: use switch for nodekey * fix: import * docs: update CLAUDE.md * fix: spacing
1 parent 2505c2b commit f511643

File tree

8 files changed

+103
-131
lines changed

8 files changed

+103
-131
lines changed

CLAUDE.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ The tool supports configuration via:
147147
f.IntVar(&count, "count", 0, "description")
148148
```
149149

150+
### Cobra Command Arguments
151+
- Prefer to use Cobra built-in validators (`cobra.NoArgs`, `cobra.ExactArgs(n)`, `cobra.MinimumNArgs(n)`, `cobra.MaximumNArgs(n)`, `cobra.ArbitraryArgs`) instead of custom `Args: func(cmd *cobra.Command, args []string) error` functions, and move argument parsing/validation logic to `PreRunE` hook
152+
150153
### Cobra Commands
151154
- Command `Short` descriptions: sentence case with ending period, e.g., `"Generate a node list to seed a node."`
152155
- Command `Long` descriptions: consider using embedded usage.md file via `//go:embed usage.md` pattern; when using inline strings, use sentence case with ending period for complete sentences

cmd/dbbench/dbbench.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,18 @@ var DBBenchCmd = &cobra.Command{
140140
Use: "dbbench [flags]",
141141
Short: "Perform a level/pebble db benchmark.",
142142
Long: usage,
143+
Args: cobra.NoArgs,
144+
PreRunE: func(cmd *cobra.Command, args []string) error {
145+
var err error
146+
sizeDistribution, err = parseRawSizeDistribution(rawSizeDistribution)
147+
if err != nil {
148+
return err
149+
}
150+
if keySize > 64 {
151+
return fmt.Errorf("max supported key size is 64 bytes. %d is too big", keySize)
152+
}
153+
return nil
154+
},
143155
RunE: func(cmd *cobra.Command, args []string) error {
144156
log.Info().Msg("Starting db test")
145157
var kvdb KeyValueDB
@@ -217,17 +229,6 @@ var DBBenchCmd = &cobra.Command{
217229

218230
return printSummary(trs)
219231
},
220-
Args: func(cmd *cobra.Command, args []string) error {
221-
var err error
222-
sizeDistribution, err = parseRawSizeDistribution(rawSizeDistribution)
223-
if err != nil {
224-
return err
225-
}
226-
if keySize > 64 {
227-
return fmt.Errorf(" max supported key size is 64 bytes. %d is too big", keySize)
228-
}
229-
return nil
230-
},
231232
}
232233

233234
func printSummary(trs []*TestResult) error {

cmd/dumpblocks/dumpblocks.go

Lines changed: 41 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,13 @@ var DumpblocksCmd = &cobra.Command{
5555
Use: "dumpblocks start end",
5656
Short: "Export a range of blocks from a JSON-RPC endpoint.",
5757
Long: usage,
58+
Args: cobra.MinimumNArgs(2),
5859
PersistentPreRun: func(cmd *cobra.Command, args []string) {
5960
rpcUrlFlagValue := flag_loader.GetRpcUrlFlagValue(cmd)
6061
inputDumpblocks.RpcUrl = *rpcUrlFlagValue
6162
},
6263
PreRunE: func(cmd *cobra.Command, args []string) error {
63-
return checkFlags()
64+
return checkFlags(args)
6465
},
6566
RunE: func(cmd *cobra.Command, args []string) error {
6667
ctx := cmd.Context()
@@ -136,50 +137,6 @@ var DumpblocksCmd = &cobra.Command{
136137
wg.Wait()
137138
log.Info().Msg("Done")
138139

139-
return nil
140-
},
141-
Args: func(cmd *cobra.Command, args []string) error {
142-
if len(args) < 2 {
143-
return fmt.Errorf("command needs at least two arguments. A start block and an end block")
144-
}
145-
146-
start, err := strconv.ParseInt(args[0], 10, 64)
147-
if err != nil {
148-
return err
149-
}
150-
end, err := strconv.ParseInt(args[1], 10, 64)
151-
if err != nil {
152-
return err
153-
}
154-
if start < 0 || end < 0 {
155-
return fmt.Errorf("the start and end parameters need to be positive")
156-
}
157-
if end < start {
158-
start, end = end, start
159-
}
160-
161-
inputDumpblocks.Start = uint64(start)
162-
inputDumpblocks.End = uint64(end)
163-
164-
if inputDumpblocks.Threads == 0 {
165-
inputDumpblocks.Threads = 1
166-
}
167-
if !slices.Contains([]string{"json", "proto"}, inputDumpblocks.Mode) {
168-
return fmt.Errorf("output format must one of [json, proto]")
169-
}
170-
171-
if err := json.Unmarshal([]byte(inputDumpblocks.FilterStr), &inputDumpblocks.filter); err != nil {
172-
return fmt.Errorf("could not unmarshal filter string")
173-
}
174-
175-
// Make sure the filters are all lowercase.
176-
for i := 0; i < len(inputDumpblocks.filter.To); i++ {
177-
inputDumpblocks.filter.To[i] = strings.ToLower(inputDumpblocks.filter.To[i])
178-
}
179-
for i := 0; i < len(inputDumpblocks.filter.From); i++ {
180-
inputDumpblocks.filter.From[i] = strings.ToLower(inputDumpblocks.filter.From[i])
181-
}
182-
183140
return nil
184141
},
185142
}
@@ -196,12 +153,50 @@ func init() {
196153
f.StringVarP(&inputDumpblocks.FilterStr, "filter", "F", "{}", "filter output based on tx to and from (not setting a filter means all are allowed)")
197154
}
198155

199-
func checkFlags() error {
156+
func checkFlags(args []string) error {
200157
// Check rpc url flag.
201158
if err := util.ValidateUrl(inputDumpblocks.RpcUrl); err != nil {
202159
return err
203160
}
204161

162+
// Parse start and end blocks
163+
start, err := strconv.ParseInt(args[0], 10, 64)
164+
if err != nil {
165+
return err
166+
}
167+
end, err := strconv.ParseInt(args[1], 10, 64)
168+
if err != nil {
169+
return err
170+
}
171+
if start < 0 || end < 0 {
172+
return fmt.Errorf("the start and end parameters need to be positive")
173+
}
174+
if end < start {
175+
start, end = end, start
176+
}
177+
178+
inputDumpblocks.Start = uint64(start)
179+
inputDumpblocks.End = uint64(end)
180+
181+
if inputDumpblocks.Threads == 0 {
182+
inputDumpblocks.Threads = 1
183+
}
184+
if !slices.Contains([]string{"json", "proto"}, inputDumpblocks.Mode) {
185+
return fmt.Errorf("output format must one of [json, proto]")
186+
}
187+
188+
if err := json.Unmarshal([]byte(inputDumpblocks.FilterStr), &inputDumpblocks.filter); err != nil {
189+
return fmt.Errorf("could not unmarshal filter string")
190+
}
191+
192+
// Make sure the filters are all lowercase.
193+
for i := 0; i < len(inputDumpblocks.filter.To); i++ {
194+
inputDumpblocks.filter.To[i] = strings.ToLower(inputDumpblocks.filter.To[i])
195+
}
196+
for i := 0; i < len(inputDumpblocks.filter.From); i++ {
197+
inputDumpblocks.filter.From[i] = strings.ToLower(inputDumpblocks.filter.From[i])
198+
}
199+
205200
return nil
206201
}
207202

cmd/foldtrace/foldtrace.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ var FoldTraceCmd = &cobra.Command{
3737
Use: "fold-trace",
3838
Short: "Trace an execution trace and fold it for visualization.",
3939
Long: usage,
40+
Args: cobra.ArbitraryArgs,
41+
PreRunE: func(cmd *cobra.Command, args []string) error {
42+
return validateInputMetric()
43+
},
4044
RunE: func(cmd *cobra.Command, args []string) error {
4145
data, err := getInputData(cmd, args)
4246
if err != nil {
@@ -100,13 +104,6 @@ var FoldTraceCmd = &cobra.Command{
100104

101105
return nil
102106
},
103-
Args: func(cmd *cobra.Command, args []string) error {
104-
err := validateInputMetric()
105-
if err != nil {
106-
return err
107-
}
108-
return nil
109-
},
110107
}
111108

112109
func getActualUsedGas(index int, td *TraceData) uint64 {

cmd/fork/fork.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ var ForkCmd = &cobra.Command{
2828
Use: "fork blockhash url",
2929
Short: "Take a forked block and walk up the chain to do analysis.",
3030
Long: "",
31+
Args: cobra.ExactArgs(2),
32+
PreRunE: func(cmd *cobra.Command, args []string) error {
33+
blockHash = ethcommon.HexToHash(args[0])
34+
rpcURL = args[1]
35+
return nil
36+
},
3137
RunE: func(cmd *cobra.Command, args []string) error {
3238
log.Info().Str("rpc", rpcURL).Str("blockHash", blockHash.String()).Msg("Starting Analysis")
3339
c, err := ethclient.Dial(rpcURL)
@@ -37,14 +43,6 @@ var ForkCmd = &cobra.Command{
3743
}
3844
return walkTheBlocks(blockHash, c)
3945
},
40-
Args: func(cmd *cobra.Command, args []string) error {
41-
if len(args) != 2 {
42-
return fmt.Errorf("two arguments required a block hash and an RPC URL")
43-
}
44-
blockHash = ethcommon.HexToHash(args[0])
45-
rpcURL = args[1]
46-
return nil
47-
},
4846
}
4947

5048
func walkTheBlocks(inputBlockHash ethcommon.Hash, client *ethclient.Client) error {

cmd/nodekey/nodekey.go

Lines changed: 25 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222
libp2ppeer "github.com/libp2p/go-libp2p/core/peer"
2323
"github.com/spf13/cobra"
2424
"github.com/spf13/pflag"
25-
"golang.org/x/exp/slices"
2625
)
2726

2827
// libp2p (substrate/avail) - https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md
@@ -66,10 +65,35 @@ var NodekeyCmd = &cobra.Command{
6665
Use: "nodekey",
6766
Short: "Generate node keys for different blockchain clients and protocols.",
6867
Long: usage,
68+
Args: cobra.NoArgs,
6969
PersistentPreRun: func(cmd *cobra.Command, args []string) {
7070
privateKey := flag_loader.GetPrivateKeyFlagValue(cmd)
7171
inputNodeKeyPrivateKey = *privateKey
7272
},
73+
PreRunE: func(cmd *cobra.Command, args []string) error {
74+
switch inputNodeKeyProtocol {
75+
case "devp2p":
76+
invalidFlags := []string{"seed", "marshal-protobuf"}
77+
return validateNodeKeyFlags(cmd, invalidFlags)
78+
case "libp2p":
79+
invalidFlags := []string{"file", "ip", "tcp", "udp", "sign", "seed"}
80+
return validateNodeKeyFlags(cmd, invalidFlags)
81+
case "seed-libp2p":
82+
invalidFlags := []string{"file", "ip", "tcp", "udp", "sign"}
83+
if err := validateNodeKeyFlags(cmd, invalidFlags); err != nil {
84+
return err
85+
}
86+
if inputNodeKeyType == "rsa" {
87+
return fmt.Errorf("the RSA key type doesn't support manual key seeding")
88+
}
89+
if inputNodeKeyType == "secp256k1" {
90+
return fmt.Errorf("the secp256k1 key type doesn't support manual key seeding")
91+
}
92+
return nil
93+
default:
94+
return fmt.Errorf("the protocol %s is not implemented", inputNodeKeyProtocol)
95+
}
96+
},
7397
RunE: func(cmd *cobra.Command, args []string) error {
7498
var nko nodeKeyOut
7599
var withSeed bool
@@ -115,46 +139,6 @@ var NodekeyCmd = &cobra.Command{
115139

116140
return nil
117141
},
118-
Args: func(cmd *cobra.Command, args []string) error {
119-
if len(args) != 0 {
120-
return fmt.Errorf("this command expects no arguments")
121-
}
122-
123-
validProtocols := []string{"devp2p", "libp2p", "seed-libp2p"}
124-
ok := slices.Contains(validProtocols, inputNodeKeyProtocol)
125-
if !ok {
126-
return fmt.Errorf("the protocol %s is not implemented", inputNodeKeyProtocol)
127-
}
128-
129-
if inputNodeKeyProtocol == "devp2p" {
130-
invalidFlags := []string{"seed", "marshal-protobuf"}
131-
err := validateNodeKeyFlags(cmd, invalidFlags)
132-
if err != nil {
133-
return err
134-
}
135-
}
136-
if inputNodeKeyProtocol == "libp2p" {
137-
invalidFlags := []string{"file", "ip", "tcp", "udp", "sign", "seed"}
138-
err := validateNodeKeyFlags(cmd, invalidFlags)
139-
if err != nil {
140-
return err
141-
}
142-
}
143-
if inputNodeKeyProtocol == "seed-libp2p" {
144-
invalidFlags := []string{"file", "ip", "tcp", "udp", "sign"}
145-
err := validateNodeKeyFlags(cmd, invalidFlags)
146-
if err != nil {
147-
return err
148-
}
149-
if inputNodeKeyType == "rsa" {
150-
return fmt.Errorf("the RSA key type doesn't support manual key seeding")
151-
}
152-
if inputNodeKeyType == "secp256k1" {
153-
return fmt.Errorf("the secp256k1 key type doesn't support manual key seeding")
154-
}
155-
}
156-
return nil
157-
},
158142
}
159143

160144
func validateNodeKeyFlags(cmd *cobra.Command, invalidFlags []string) error {

cmd/wallet/wallet.go

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,17 @@ var (
2929

3030
// WalletCmd represents the wallet command
3131
var WalletCmd = &cobra.Command{
32-
Use: "wallet [create|inspect]",
33-
Short: "Create or inspect BIP39(ish) wallets.",
34-
Long: usage,
32+
Use: "wallet [create|inspect]",
33+
Short: "Create or inspect BIP39(ish) wallets.",
34+
Long: usage,
35+
Args: cobra.ExactArgs(1),
36+
ValidArgs: []string{"create", "inspect"},
37+
PreRunE: func(cmd *cobra.Command, args []string) error {
38+
if args[0] != "create" && args[0] != "inspect" {
39+
return fmt.Errorf("expected argument to be create or inspect. Got: %s", args[0])
40+
}
41+
return nil
42+
},
3543
RunE: func(cmd *cobra.Command, args []string) error {
3644
mode := args[0]
3745
var err error
@@ -91,15 +99,6 @@ var WalletCmd = &cobra.Command{
9199
fmt.Println(string(out))
92100
return nil
93101
},
94-
Args: func(cmd *cobra.Command, args []string) error {
95-
if len(args) != 1 {
96-
return fmt.Errorf("expected exactly one argument: create or inspect")
97-
}
98-
if args[0] != "create" && args[0] != "inspect" {
99-
return fmt.Errorf("expected argument to be create or inspect. Got: %s", args[0])
100-
}
101-
return nil
102-
},
103102
}
104103

105104
func getFileOrFlag(filename string, flag string) (string, error) {

cmd/wrapcontract/wrapcontract.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,7 @@ var WrapContractCmd = &cobra.Command{
3737
fmt.Println(createBytecode)
3838
return nil
3939
},
40-
Args: func(cmd *cobra.Command, args []string) error {
41-
if len(args) > 1 {
42-
return fmt.Errorf("expected at most one argument: bytecode")
43-
}
44-
return nil
45-
},
40+
Args: cobra.MaximumNArgs(1),
4641
}
4742

4843
func init() {

0 commit comments

Comments
 (0)