kaspad/cmd/kaspactl/command_parser.go
Isaac Cook 9b23bbcdb5
kaspactl: string slice deser for GetUtxosByAddresses (#1955)
Co-authored-by: Ori Newman <orinewman1@gmail.com>
2022-02-24 00:40:01 +02:00

189 lines
5.4 KiB
Go

package main
import (
"reflect"
"strconv"
"strings"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/server/grpcserver/protowire"
"github.com/pkg/errors"
)
func parseCommand(args []string, commandDescs []*commandDescription) (*protowire.KaspadMessage, error) {
commandName, parameterStrings := args[0], args[1:]
var commandDesc *commandDescription
for _, cd := range commandDescs {
if cd.name == commandName {
commandDesc = cd
break
}
}
if commandDesc == nil {
return nil, errors.Errorf("unknown command: %s. Use --list-commands to list all commands", commandName)
}
if len(parameterStrings) != len(commandDesc.parameters) {
return nil, errors.Errorf("command '%s' expects %d parameters but got %d",
commandName, len(commandDesc.parameters), len(parameterStrings))
}
commandValue := reflect.New(unwrapCommandType(commandDesc.typeof))
for i, parameterDesc := range commandDesc.parameters {
parameterValue, err := stringToValue(parameterDesc, parameterStrings[i])
if err != nil {
return nil, err
}
setField(commandValue, parameterValue, parameterDesc)
}
return generateKaspadMessage(commandValue, commandDesc)
}
func setField(commandValue reflect.Value, parameterValue reflect.Value, parameterDesc *parameterDescription) {
parameterField := commandValue.Elem().FieldByName(parameterDesc.name)
parameterField.Set(parameterValue)
}
func stringToValue(parameterDesc *parameterDescription, valueStr string) (reflect.Value, error) {
if valueStr == "-" {
return reflect.Zero(parameterDesc.typeof), nil
}
var value interface{}
var err error
switch parameterDesc.typeof.Kind() {
case reflect.Bool:
value, err = strconv.ParseBool(valueStr)
if err != nil {
return reflect.Value{}, errors.WithStack(err)
}
case reflect.Int8:
var valueInt64 int64
valueInt64, err = strconv.ParseInt(valueStr, 10, 8)
if err != nil {
return reflect.Value{}, errors.WithStack(err)
}
value = int8(valueInt64)
case reflect.Int16:
var valueInt64 int64
valueInt64, err = strconv.ParseInt(valueStr, 10, 16)
if err != nil {
return reflect.Value{}, errors.WithStack(err)
}
value = int16(valueInt64)
case reflect.Int32:
var valueInt64 int64
valueInt64, err = strconv.ParseInt(valueStr, 10, 32)
if err != nil {
return reflect.Value{}, errors.WithStack(err)
}
value = int32(valueInt64)
case reflect.Int64:
value, err = strconv.ParseInt(valueStr, 10, 64)
if err != nil {
return reflect.Value{}, errors.WithStack(err)
}
case reflect.Uint8:
var valueUInt64 uint64
valueUInt64, err = strconv.ParseUint(valueStr, 10, 8)
if err != nil {
return reflect.Value{}, errors.WithStack(err)
}
value = uint8(valueUInt64)
case reflect.Uint16:
var valueUInt64 uint64
valueUInt64, err = strconv.ParseUint(valueStr, 10, 16)
if err != nil {
return reflect.Value{}, errors.WithStack(err)
}
value = uint16(valueUInt64)
case reflect.Uint32:
var valueUInt64 uint64
valueUInt64, err = strconv.ParseUint(valueStr, 10, 32)
if err != nil {
return reflect.Value{}, errors.WithStack(err)
}
value = uint32(valueUInt64)
case reflect.Uint64:
value, err = strconv.ParseUint(valueStr, 10, 64)
if err != nil {
return reflect.Value{}, errors.WithStack(err)
}
case reflect.Float32:
var valueFloat64 float64
valueFloat64, err = strconv.ParseFloat(valueStr, 32)
if err != nil {
return reflect.Value{}, errors.WithStack(err)
}
value = float32(valueFloat64)
case reflect.Float64:
value, err = strconv.ParseFloat(valueStr, 64)
if err != nil {
return reflect.Value{}, errors.WithStack(err)
}
case reflect.String:
value = valueStr
case reflect.Struct:
pointer := reflect.New(parameterDesc.typeof) // create pointer to this type
fieldInterface := pointer.Interface().(proto.Message)
err := protojson.Unmarshal([]byte(valueStr), fieldInterface)
if err != nil {
return reflect.Value{}, errors.WithStack(err)
}
// Unpointer the value once it's ready
fieldInterfaceValue := reflect.ValueOf(fieldInterface)
value = fieldInterfaceValue.Elem().Interface()
case reflect.Ptr:
dummyParameterDesc := &parameterDescription{
name: "valuePointedTo",
typeof: parameterDesc.typeof.Elem(),
}
valuePointedTo, err := stringToValue(dummyParameterDesc, valueStr)
if err != nil {
return reflect.Value{}, errors.WithStack(err)
}
pointer := pointerToValue(valuePointedTo)
value = pointer.Interface()
case reflect.Slice:
sliceType := parameterDesc.typeof.Elem()
if sliceType.Kind() != reflect.String {
return reflect.Value{},
errors.Errorf("Unsupported slice type '%s' for parameter '%s'",
sliceType,
parameterDesc.name)
}
if valueStr == "" {
value = []string{}
} else {
value = strings.Split(valueStr, ",")
}
// Int and uint are not supported because their size is platform-dependant
case reflect.Int,
reflect.Uint,
// Other types are not supported simply because they are not used in any command right now
// but support can be added if and when needed
reflect.Func,
reflect.Interface,
reflect.Map,
reflect.UnsafePointer,
reflect.Invalid,
reflect.Uintptr,
reflect.Complex64,
reflect.Complex128,
reflect.Array,
reflect.Chan:
fallthrough
default:
return reflect.Value{},
errors.Errorf("Unsupported type '%s' for parameter '%s'", parameterDesc.typeof.Kind(), parameterDesc.name)
}
return reflect.ValueOf(value), nil
}