cosmossdk.io/client/v2@v2.0.0-beta.1/autocli/flag/messager_binder.go (about)

     1  package flag
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/spf13/cobra"
     7  	"github.com/spf13/pflag"
     8  	"google.golang.org/protobuf/reflect/protoreflect"
     9  )
    10  
    11  // SignerInfo contains information about the signer field.
    12  // That field is special because it needs to be known for signing.
    13  // This struct keeps track of the field name and whether it is a flag.
    14  // IsFlag and PositionalArgIndex are mutually exclusive.
    15  type SignerInfo struct {
    16  	PositionalArgIndex int
    17  	IsFlag             bool
    18  	FieldName          string
    19  }
    20  
    21  // MessageBinder binds multiple flags in a flag set to a protobuf message.
    22  type MessageBinder struct {
    23  	CobraArgs  cobra.PositionalArgs
    24  	SignerInfo SignerInfo
    25  
    26  	positionalFlagSet *pflag.FlagSet
    27  	positionalArgs    []fieldBinding
    28  	hasVarargs        bool
    29  	hasOptional       bool
    30  	mandatoryArgUntil int
    31  
    32  	flagBindings []fieldBinding
    33  	messageType  protoreflect.MessageType
    34  }
    35  
    36  // BuildMessage builds and returns a new message for the bound flags.
    37  func (m MessageBinder) BuildMessage(positionalArgs []string) (protoreflect.Message, error) {
    38  	msg := m.messageType.New()
    39  	err := m.Bind(msg, positionalArgs)
    40  	return msg, err
    41  }
    42  
    43  // Bind binds the flag values to an existing protobuf message.
    44  func (m MessageBinder) Bind(msg protoreflect.Message, positionalArgs []string) error {
    45  	// first set positional args in the positional arg flag set
    46  	n := len(positionalArgs)
    47  	for i := range m.positionalArgs {
    48  		if i == n {
    49  			break
    50  		}
    51  
    52  		name := fmt.Sprintf("%d", i)
    53  		if i == m.mandatoryArgUntil && m.hasVarargs {
    54  			for _, v := range positionalArgs[i:] {
    55  				if err := m.positionalFlagSet.Set(name, v); err != nil {
    56  					return err
    57  				}
    58  			}
    59  		} else {
    60  			if err := m.positionalFlagSet.Set(name, positionalArgs[i]); err != nil {
    61  				return err
    62  			}
    63  		}
    64  	}
    65  
    66  	// bind positional arg values to the message
    67  	for _, arg := range m.positionalArgs {
    68  		if err := arg.bind(msg); err != nil {
    69  			return err
    70  		}
    71  	}
    72  
    73  	// bind flag values to the message
    74  	for _, binding := range m.flagBindings {
    75  		if err := binding.bind(msg); err != nil {
    76  			return err
    77  		}
    78  	}
    79  
    80  	return nil
    81  }
    82  
    83  // Get calls BuildMessage and wraps the result in a protoreflect.Value.
    84  func (m MessageBinder) Get(protoreflect.Value) (protoreflect.Value, error) {
    85  	msg, err := m.BuildMessage(nil)
    86  	return protoreflect.ValueOfMessage(msg), err
    87  }
    88  
    89  type fieldBinding struct {
    90  	hasValue HasValue
    91  	field    protoreflect.FieldDescriptor
    92  }
    93  
    94  func (f fieldBinding) bind(msg protoreflect.Message) error {
    95  	field := f.field
    96  	val, err := f.hasValue.Get(msg.NewField(field))
    97  	if err != nil {
    98  		return err
    99  	}
   100  
   101  	if field.IsMap() {
   102  		return nil
   103  	}
   104  
   105  	if msg.IsValid() && val.IsValid() {
   106  		msg.Set(f.field, val)
   107  	}
   108  
   109  	return nil
   110  }