github.com/df-mc/dragonfly@v0.9.13/server/cmd/parameter.go (about)

     1  package cmd
     2  
     3  import (
     4  	"github.com/go-gl/mathgl/mgl64"
     5  	"reflect"
     6  	"strings"
     7  )
     8  
     9  // Parameter is an interface for a generic parameters. Users may have types as command parameters that
    10  // implement this parameter.
    11  type Parameter interface {
    12  	// Parse takes an arbitrary amount of arguments from the command Line passed and parses it, so that it can
    13  	// store it to value v. If the arguments cannot be parsed from the Line, an error should be returned.
    14  	Parse(line *Line, v reflect.Value) error
    15  	// Type returns the type of the parameter. It will show up in the usage of the command, and, if one of the
    16  	// known type names, will also show up client-side.
    17  	Type() string
    18  }
    19  
    20  // Enum is an interface for enum-type parameters. Users may have types as command parameters that implement
    21  // this parameter in order to allow a specific set of options only.
    22  // Enum implementations must be of the type string, for example:
    23  //
    24  //	type GameMode string
    25  //	func (GameMode) Type() string { return "GameMode" }
    26  //	func (GameMode) Options(Source) []string { return []string{"survival", "creative"} }
    27  //
    28  // Their values will then automatically be set to whichever option returned in Enum.Options is selected by
    29  // the user.
    30  type Enum interface {
    31  	// Type returns the type of the enum. This type shows up client-side in the command usage, in the spot
    32  	// where parameter types otherwise are.
    33  	// Type names returned are used as an identifier for this enum type. Different Enum implementations must
    34  	// return a different string in the Type method.
    35  	Type() string
    36  	// Options should return a list of options that show up on the client side. The command will ensure that
    37  	// the argument passed to the enum parameter will be equal to one of these options. The provided Source
    38  	// can also be used to change the enums for each player.
    39  	Options(source Source) []string
    40  }
    41  
    42  // SubCommand represents a subcommand that may be added as a static value that must be written. Adding
    43  // multiple Runnable implementations to the command in New with different SubCommand fields as the
    44  // first parameter allows for commands with subcommands.
    45  type SubCommand struct{}
    46  
    47  // Varargs is an argument type that may be used to capture all arguments that follow. This is useful for,
    48  // for example, messages and names.
    49  type Varargs string
    50  
    51  // Optional is an argument type that may be used to make any of the available parameter types optional. Optional command
    52  // parameters may only occur at the end of the Runnable struct. No non-optional parameter is allowed after an optional
    53  // parameter.
    54  type Optional[T any] struct {
    55  	val T
    56  	set bool
    57  }
    58  
    59  // Load returns the value specified upon executing the command and a bool that is true if the parameter was filled out
    60  // by the Source.
    61  func (o Optional[T]) Load() (T, bool) {
    62  	return o.val, o.set
    63  }
    64  
    65  // LoadOr returns the value specified upon executing the command, or a value 'or' if the parameter was not filled out
    66  // by the Source.
    67  func (o Optional[T]) LoadOr(or T) T {
    68  	if o.set {
    69  		return o.val
    70  	}
    71  	return or
    72  }
    73  
    74  // with returns an Optional[T] with the value passed. It also sets the 'set' field to true.
    75  func (o Optional[T]) with(val any) any {
    76  	return Optional[T]{val: val.(T), set: true}
    77  }
    78  
    79  // optionalT is used to identify a parameter of the Optional type.
    80  type optionalT interface {
    81  	with(val any) any
    82  }
    83  
    84  // typeNameOf returns a readable type name for the interface value passed. If none could be found, 'value'
    85  // is returned.
    86  func typeNameOf(i any, name string) string {
    87  	switch i.(type) {
    88  	case int, int8, int16, int32, int64:
    89  		return "int"
    90  	case uint, uint8, uint16, uint32, uint64:
    91  		return "uint"
    92  	case float32, float64:
    93  		return "float"
    94  	case string:
    95  		return "string"
    96  	case Varargs:
    97  		return "text"
    98  	case bool:
    99  		return "bool"
   100  	case mgl64.Vec3:
   101  		return "x y z"
   102  	case []Target:
   103  		return "target"
   104  	case SubCommand:
   105  		return name
   106  	}
   107  	if param, ok := i.(Parameter); ok {
   108  		return param.Type()
   109  	}
   110  	if enum, ok := i.(Enum); ok {
   111  		return enum.Type()
   112  	}
   113  	return "value"
   114  }
   115  
   116  // unwrap returns the underlying reflect.Value of a reflect.Value, assuming it is of the Optional[T] type.
   117  func unwrap(v reflect.Value) reflect.Value {
   118  	if _, ok := v.Interface().(optionalT); ok {
   119  		return reflect.New(v.Field(0).Type()).Elem()
   120  	}
   121  	return v
   122  }
   123  
   124  // optional checks if the reflect.Value passed implements the optionalT interface.
   125  func optional(v reflect.Value) bool {
   126  	_, ok := v.Interface().(optionalT)
   127  	return ok
   128  }
   129  
   130  // suffix returns the suffix of the parameter as set in the struct field, if any.
   131  func suffix(v reflect.StructField) string {
   132  	_, str := tag(v)
   133  	return str
   134  }
   135  
   136  // name returns the name of the parameter as set in the struct tag if it exists, or the field's name if not.
   137  func name(v reflect.StructField) string {
   138  	str, _ := tag(v)
   139  	if str == "" {
   140  		return v.Name
   141  	}
   142  	return str
   143  }
   144  
   145  // tag returns the name and suffix as specified in the 'cmd' tag, or empty strings if not present.
   146  func tag(v reflect.StructField) (name string, suffix string) {
   147  	t, _ := v.Tag.Lookup("cmd")
   148  	a, b, _ := strings.Cut(t, ",")
   149  	return a, b
   150  }