adding envs tagging/interpolation
This commit is contained in:
2
exec_extra/TODO
Normal file
2
exec_extra/TODO
Normal file
@@ -0,0 +1,2 @@
|
||||
- for GetCmdFromStruct, support []byte fields
|
||||
-- support hex and base64 struct field opts (and others?) via `enc=` struct tag.
|
||||
@@ -1,34 +1,123 @@
|
||||
package exec_extra
|
||||
|
||||
var (
|
||||
StructTagCmdArgs string = "cmdarg"
|
||||
import (
|
||||
`r00t2.io/goutils/bitmask`
|
||||
)
|
||||
|
||||
var (
|
||||
CmdArgsTag string = "cmdarg"
|
||||
/*
|
||||
CmdArgsOptPreferShort, if specified, prefers the "short" argument over "long" if both are specified.
|
||||
The default is to prefer long.
|
||||
CmdArgsDictSep specifies the string to use to separate keys and values.
|
||||
|
||||
Can be specified per-field via the `prefer_short` option (no value/value ignored).
|
||||
To override at the struct field level, use the tag value:
|
||||
|
||||
`<CmdArgsTag>:"dictsep=<str>"`
|
||||
|
||||
Where str is the string to use. e.g.:
|
||||
|
||||
`cmdarg:"short=d,long=data,dictsep=."`
|
||||
|
||||
Would render a map value of map[string]string{"foo": "bar"} as:
|
||||
|
||||
`-d foo.bar`
|
||||
*/
|
||||
CmdArgsOptPreferShort cmdArgOpt = func(opts *cmdArgsOpts) (err error) {
|
||||
opts.preferShort = true
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
CmdArgsOptShortEquals, if specified, renders short flags *with* an equals sign
|
||||
(if using POSIX args).
|
||||
|
||||
Has no effect if using Windows traditional syntax or if there is no value for the field.
|
||||
*/
|
||||
CmdArgsOptShortEquals cmdArgOpt = func(opts *cmdArgsOpts) (err error) {
|
||||
opts.preferShort = true
|
||||
return
|
||||
}
|
||||
|
||||
CmdArgsOptLongNoEquals cmdArgOpt = func(opts *cmdArgsOpts) (err error) {
|
||||
opts.preferShort = true
|
||||
return
|
||||
}
|
||||
CmdArgsDictSep string = ":"
|
||||
)
|
||||
|
||||
// CmdArgOptNone is an "empty option" and does nothing.
|
||||
const CmdArgOptNone bitmask.MaskBit = 0
|
||||
const (
|
||||
/*
|
||||
CmdArgOptPreferShort prefers short options where possible.
|
||||
Has no effect if Windows traditional syntax is used.
|
||||
|
||||
The default is to use long options.
|
||||
See also CmdArgOptPreferLong.
|
||||
|
||||
Corresponding struct tag option: prefer_short
|
||||
*/
|
||||
CmdArgOptPreferShort cmdArgOpt = 1 << iota
|
||||
/*
|
||||
CmdArgOptPreferLong prefers long options where possible.
|
||||
Has no effect if Windows traditional syntax is used.
|
||||
|
||||
This behavior is the default, but it can be used to
|
||||
override a CmdArgOptPreferShort from a parent.
|
||||
|
||||
Corresponding struct tag option: prefer_long
|
||||
*/
|
||||
CmdArgOptPreferLong
|
||||
/*
|
||||
CmdArgOptShortEquals will use an equals separator
|
||||
for short flags instead of a space (the default).
|
||||
Has no effect if Windows traditional syntax is used.
|
||||
|
||||
Corresponding struct tag option: short_equals
|
||||
*/
|
||||
CmdArgOptShortEquals
|
||||
/*
|
||||
CmdArgOptShortNoEquals will use a space separator
|
||||
for short flags instead of an equals.
|
||||
Has no effect if Windows traditional syntax is used.
|
||||
|
||||
This behavior is the default, but it can be used to
|
||||
override a CmdArgOptPreferShort from a parent.
|
||||
|
||||
Corresponding struct tag option: no_short_equals
|
||||
*/
|
||||
CmdArgOptShortNoEquals
|
||||
/*
|
||||
CmdArgOptLongEquals will use an equals separator
|
||||
for long flags instead of a space.
|
||||
Has no effect if Windows traditional syntax is used.
|
||||
|
||||
This behavior is the default, but it can be used to
|
||||
override a CmdArgOptLongNoEquals from a parent.
|
||||
|
||||
Corresponding struct tag option: long_equals
|
||||
*/
|
||||
CmdArgOptLongEquals
|
||||
/*
|
||||
CmdArgOptLongNoEquals will use a space separator
|
||||
for short flags instead of an equals.
|
||||
Has no effect if Windows traditional syntax is used.
|
||||
|
||||
This behavior is the default, but it can be used to
|
||||
override a CmdArgOptPreferShort from a parent.
|
||||
|
||||
Corresponding struct tag option: no_long_equals
|
||||
*/
|
||||
CmdArgOptLongNoEquals
|
||||
/*
|
||||
CmdArgOptForceNoPosix forces the resulting command string to use "traditional Windows" flag notation.
|
||||
|
||||
Traditionally, Windows used flags like `/f` instead of POSIX `-f`, `/c:value` instead of `-c value`
|
||||
or `-c=value`, etc.
|
||||
Has no effect if not running on Windows.
|
||||
|
||||
This behavior is the default, but it can be used to
|
||||
override a CmdArgOptPreferShort from a parent.
|
||||
|
||||
See also the inverse of this option, CmdArgOptForcePosix.
|
||||
|
||||
Corresponding struct tag option: force_no_posix
|
||||
*/
|
||||
CmdArgOptForceNoPosix
|
||||
/*
|
||||
CmdArgOptForcePosix forces the resulting command string to use "POSIX" flag notation.
|
||||
|
||||
Traditionally, Windows used flags like `/f` instead of POSIX `-f`, `/c:value` instead of `-c value`
|
||||
or `-c=value`, etc.
|
||||
|
||||
If this option is passed, then the POSIX flag syntax (-a/--arg) will be used instead.
|
||||
|
||||
Note that on Windows runtime, the default is to use the traditional slash-based syntax.
|
||||
If you are generating command strings for Powershell or third-party software, you probably
|
||||
want to use CmdArgsOptForcePosix instead.
|
||||
|
||||
See also the inverse of this option, CmdArgsOptForceNoPosix.
|
||||
|
||||
Corresponding struct tag option: force_posix
|
||||
*/
|
||||
CmdArgOptForcePosix
|
||||
)
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
package exec_extra
|
||||
|
||||
var (
|
||||
/*
|
||||
CmdArgsOptForcePosix forces the resulting command string to use "POSIX-style" flag notation.
|
||||
|
||||
Traditionally, Windows used flags like `/f` instead of POSIX `-f`, `/c:value` instead of `-c value`
|
||||
or `-c=value`, etc.
|
||||
|
||||
If this option is passed, either to GetCmdFromStruct() or for a specific field via the
|
||||
tag defined by StructTagCmdArgs (option `force_posix`, no value/value ignored), then the
|
||||
POSIX-style flag syntax will be used instead.
|
||||
|
||||
Note that on Windows runtime, the default is to use the traditional slash-based syntax.
|
||||
If you are generating command strings for Powershell or third-party software, you probably
|
||||
want to use this option.
|
||||
|
||||
See also the inverse of this option, CmdArgsOptForceNoPosix.
|
||||
*/
|
||||
CmdArgsOptForcePosix cmdArgOpt = func(opts *cmdArgsOpts) (err error) {
|
||||
opts.forcePosix = true
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
CmdArgsOptForceNoPosix forces the resulting command string to use "traditional Windows" flag notation.
|
||||
|
||||
Traditionally, Windows used flags like `/f` instead of POSIX `-f`, `/c:value` instead of `-c value`
|
||||
or `-c=value`, etc.
|
||||
|
||||
If this option is passed, either to GetCmdFromStruct() or for a specific field via the
|
||||
tag defined by StructTagCmdArgs (option `force_no_posix`, no value/value ignored), then the
|
||||
Windows-style flag syntax will be used instead.
|
||||
|
||||
Note that on Windows runtime, the default is to use the traditional slash-based syntax.
|
||||
If you are generating command strings for Powershell or third-party software, you probably
|
||||
want to use CmdArgsOptForcePosix instead.
|
||||
|
||||
See also the inverse of this option, CmdArgsOptForcePosix.
|
||||
*/
|
||||
CmdArgsOptForceNoPosix cmdArgOpt = func(opts *cmdArgsOpts) (err error) {
|
||||
opts.forcePosix = false
|
||||
return
|
||||
}
|
||||
)
|
||||
@@ -19,9 +19,20 @@
|
||||
package exec_extra
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
`fmt`
|
||||
`os/exec`
|
||||
`reflect`
|
||||
|
||||
`r00t2.io/goutils/bitmask`
|
||||
`r00t2.io/goutils/structutils`
|
||||
`r00t2.io/sysutils/errs`
|
||||
)
|
||||
|
||||
/*
|
||||
ExecCmdReturn runs cmd and alsom returns the exitStatus.
|
||||
|
||||
A non-zero exit status is not treated as an error.
|
||||
*/
|
||||
func ExecCmdReturn(cmd *exec.Cmd) (exitStatus int, err error) {
|
||||
// https://stackoverflow.com/a/55055100/733214
|
||||
err = cmd.Run()
|
||||
@@ -31,14 +42,12 @@ func ExecCmdReturn(cmd *exec.Cmd) (exitStatus int, err error) {
|
||||
}
|
||||
|
||||
/*
|
||||
GetCmdFromStruct takes (a pointer to) a struct and returns a slice of
|
||||
GetCmdFromStruct takes a pointer to a struct and returns a slice of
|
||||
strings compatible with os/exec.Cmd.
|
||||
|
||||
The tag name used can be changed by setting the StructTagCmdArgs variable in this module;
|
||||
The tag name used can be changed by setting the CmdArgsTag variable in this module;
|
||||
the default is `cmdarg`.
|
||||
|
||||
If the tag value is "-", the field will be skipped. Any other tag value(s) are ignored.
|
||||
|
||||
Tag value format:
|
||||
<tag>:"<option>=<value>[,<option>[=<value>],<option>[=<value>]...]"
|
||||
e.g.
|
||||
@@ -46,41 +55,301 @@ func ExecCmdReturn(cmd *exec.Cmd) (exitStatus int, err error) {
|
||||
cmdarg:"short=l"
|
||||
cmdarg:"long=list"
|
||||
|
||||
If the tag value is "-", or <VAR NAME> is not provided, the field will be explicitly skipped.
|
||||
If the tag value is "-", the field will be explicitly skipped.
|
||||
(This is the default behavior for struct fields not tagged with `cmdarg`.)
|
||||
If the field is nil, it will be skipped.
|
||||
|
||||
If a cmdarg tag is specified but has no `short` or `long` option value, the field will be skipped entirely.
|
||||
If a field's value is nil, it will be skipped.
|
||||
Otherwise if a field's value is the zero-value, it will be skipped.
|
||||
|
||||
Recognized options:
|
||||
Aside from the 'short' and 'long' tag valued-options, see the comment for each CmdArgOpt* constant
|
||||
for their corresponding tag option and the CmdArgs* variables as well for their corresponding tag option.
|
||||
|
||||
* short - A short flag for the argument
|
||||
Each struct field can be one of the following types:
|
||||
|
||||
e.g.:
|
||||
* string
|
||||
* *string
|
||||
* slice (with elements of supported types)
|
||||
* array (with elements of supported types)
|
||||
* map (with keys and values of supported types; see the CmdArgsDictSep variable for the separator to use)
|
||||
* struct (with fields of supported types)
|
||||
* int/int8/int16/int32/int64
|
||||
* uint/uint8/uint16/uint32/uint64
|
||||
* float32/float64
|
||||
|
||||
struct{
|
||||
// If this is an empty string, it will be replaced with the value of $CWD.
|
||||
CurrentDir string `envpop:"CWD"`
|
||||
// This would only populate with $USER if the pointer is nil.
|
||||
UserName *string `envpop:"USER"`
|
||||
// This will *always* replace the field's value with the value of $DISPLAY,
|
||||
// even if not an empty string.
|
||||
// Note the `force` option.
|
||||
Display string `envpop:"DISPLAY,force"`
|
||||
// Likewise, even if not nil, this field's value would be replaced with the value of $SHELL.
|
||||
Shell *string `envpop:"SHELL,force"`
|
||||
// This field will be untouched if non-nil, otherwise it will be a pointer to an empty string
|
||||
// if FOOBAR is undefined.
|
||||
NonExistentVar *string `envpop:"FOOBAR,allow_empty"`
|
||||
}
|
||||
Struct fields, slice/array elements, etc. are processed in order.
|
||||
Maps, because ordering is non-deterministic, may have unpredictable ordering.
|
||||
|
||||
If s is nil, nothing will be done and err will be errs.ErrNilPtr.
|
||||
If s is not a pointer to a struct, nothing will be done and err will be errs.ErrBadType.
|
||||
If s is nil, nothing will be done.
|
||||
If s is not a pointer to a struct, nothing will be done.
|
||||
*/
|
||||
func GetCmdFromStruct[T any](s T, opts ...cmdArgOpt) (cmdSlice []string, err error) {
|
||||
func GetCmdFromStruct[T any](s T, defaultOpts ...cmdArgOpt) (cmdSlice []string, err error) {
|
||||
|
||||
// TODO
|
||||
var tmpSlice []string
|
||||
var ptrVal reflect.Value
|
||||
var ptrType reflect.Type
|
||||
var ptrKind reflect.Kind
|
||||
var argFlags *cmdArgFlag
|
||||
var opts *bitmask.MaskBit = bitmask.NewMaskBit()
|
||||
var sVal reflect.Value = reflect.ValueOf(s)
|
||||
var sType reflect.Type = sVal.Type()
|
||||
var kind reflect.Kind = sType.Kind()
|
||||
|
||||
if kind != reflect.Ptr {
|
||||
return
|
||||
}
|
||||
if sVal.IsNil() || sVal.IsZero() || !sVal.IsValid() {
|
||||
return
|
||||
}
|
||||
ptrVal = sVal.Elem()
|
||||
ptrType = ptrVal.Type()
|
||||
ptrKind = ptrType.Kind()
|
||||
|
||||
if ptrKind != reflect.Struct {
|
||||
return
|
||||
}
|
||||
|
||||
tmpSlice = make([]string, 0)
|
||||
if defaultOpts != nil && len(defaultOpts) != 0 {
|
||||
for _, o := range defaultOpts {
|
||||
opts.AddFlag(o.BitMask())
|
||||
}
|
||||
}
|
||||
|
||||
argFlags = &cmdArgFlag{
|
||||
defaults: new(bitmask.MaskBit),
|
||||
fieldOpts: new(bitmask.MaskBit),
|
||||
boolMap: nil,
|
||||
strMap: nil,
|
||||
shortFlag: "",
|
||||
longFlag: "",
|
||||
field: nil,
|
||||
value: &ptrVal,
|
||||
argSlice: &tmpSlice,
|
||||
}
|
||||
*argFlags.defaults = *opts
|
||||
*argFlags.fieldOpts = *opts
|
||||
|
||||
err = getCmdStruct(argFlags)
|
||||
|
||||
cmdSlice = tmpSlice
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// getCmdStruct iterates over each field of reflect.Value struct v, and is called by GetCmdFromStruct.
|
||||
func getCmdStruct(argFlags *cmdArgFlag) (err error) {
|
||||
|
||||
var t reflect.Type
|
||||
var kind reflect.Kind
|
||||
var fieldArgFlag *cmdArgFlag
|
||||
|
||||
if argFlags == nil {
|
||||
return
|
||||
}
|
||||
if argFlags.value == nil {
|
||||
return
|
||||
}
|
||||
t = argFlags.value.Type()
|
||||
kind = t.Kind()
|
||||
|
||||
if kind != reflect.Struct {
|
||||
err = errs.ErrBadType
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < argFlags.value.NumField(); i++ {
|
||||
fieldArgFlag = new(cmdArgFlag)
|
||||
*fieldArgFlag = *argFlags
|
||||
fieldArgFlag.field = new(reflect.StructField)
|
||||
fieldArgFlag.value = new(reflect.Value)
|
||||
*fieldArgFlag.field = t.Field(i)
|
||||
*fieldArgFlag.value = argFlags.value.Field(i)
|
||||
|
||||
if err = getCmdStructField(fieldArgFlag); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// getCmdStructField parses an individual struct field.
|
||||
func getCmdStructField(argFlags *cmdArgFlag) (err error) {
|
||||
|
||||
if argFlags == nil || argFlags.field == nil || argFlags.value == nil {
|
||||
return
|
||||
}
|
||||
|
||||
argFlags.boolMap = structutils.TagToBoolMap(*argFlags.field, CmdArgsTag, structutils.TagMapTrim)
|
||||
if argFlags.boolMap["-"] {
|
||||
return
|
||||
}
|
||||
argFlags.strMap = structutils.TagToStringMap(*argFlags.field, CmdArgsTag, structutils.TagMapTrim)
|
||||
if argFlags.strMap == nil {
|
||||
return
|
||||
}
|
||||
for key, val := range argFlags.strMap {
|
||||
switch key {
|
||||
case "short":
|
||||
argFlags.shortFlag = val
|
||||
case "long":
|
||||
argFlags.longFlag = val
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(argFlags.field.Name + ":")
|
||||
fmt.Printf("BEFORE: %d\t%d\n", argFlags.defaults.Value(), argFlags.fieldOpts.Value())
|
||||
argFlags.fieldOpts = parseCmdArgOpts(argFlags.fieldOpts, argFlags.defaults, *argFlags.field)
|
||||
fmt.Printf("AFTER: %d\t%d\n\n", argFlags.defaults.Value(), argFlags.fieldOpts.Value())
|
||||
/*
|
||||
if v.Kind() == reflect.Ptr {
|
||||
if v.IsNil() {
|
||||
return
|
||||
}
|
||||
err = getCmdStructField(field, v.Elem(), current, defaults, tmpSlice)
|
||||
} else {
|
||||
err = getCmdValue(v, opts, tagVals, tmpSlice)
|
||||
}
|
||||
*/
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// getCmdValue is a dispatcher for a reflect value.
|
||||
func getCmdValue(v reflect.Value, opts *bitmask.MaskBit, flagVals map[string]string, tmpSlice *[]string) (err error) {
|
||||
|
||||
/*
|
||||
var kind reflect.Kind = v.Kind()
|
||||
|
||||
switch kind {
|
||||
case reflect.Ptr:
|
||||
if v.IsNil() || v.IsZero() || !v.IsValid() {
|
||||
return
|
||||
}
|
||||
v = v.Elem()
|
||||
if err = getCmdValue(v, opts, tmpSlice); err != nil {
|
||||
return
|
||||
}
|
||||
case reflect.String:
|
||||
if err = getCmdString(v, opts, tmpSlice); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
case reflect.Slice, reflect.Array:
|
||||
if err = getCmdSlice(v); err != nil {
|
||||
}
|
||||
case reflect.Map:
|
||||
if err = getCmdMap(v); err != nil {
|
||||
return
|
||||
}
|
||||
case reflect.Struct:
|
||||
if err = getCmdStruct(v); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// parseCmdArgOpts returns a parsed, combined, etc. set of options into a single OR'd bitmask.
|
||||
func parseCmdArgOpts(current *bitmask.MaskBit, defaults *bitmask.MaskBit, field reflect.StructField) (opts *bitmask.MaskBit) {
|
||||
|
||||
var tagOpts *bitmask.MaskBit = tagOptsToMask(field)
|
||||
|
||||
opts = defaults.Copy()
|
||||
fmt.Printf(
|
||||
"PARSE BEFORE:\n\tOPTS:\t%d\n\tCURRENT:\t%d\n\tDEFAULTS:\t%d\n\tTAGOPTS:\t%d\n",
|
||||
opts.Value(),
|
||||
)
|
||||
for _, b := range []*bitmask.MaskBit{
|
||||
current,
|
||||
tagOpts,
|
||||
} {
|
||||
if b == nil {
|
||||
continue
|
||||
}
|
||||
if b.HasFlag(CmdArgOptPreferShort.BitMask()) && !b.HasFlag(CmdArgOptPreferLong.BitMask()) {
|
||||
opts.AddFlag(CmdArgOptPreferShort.BitMask())
|
||||
opts.ClearFlag(CmdArgOptPreferLong.BitMask())
|
||||
} else {
|
||||
opts.AddFlag(CmdArgOptPreferLong.BitMask())
|
||||
opts.ClearFlag(CmdArgOptPreferShort.BitMask())
|
||||
}
|
||||
if b.HasFlag(CmdArgOptShortEquals.BitMask()) && !b.HasFlag(CmdArgOptShortNoEquals.BitMask()) {
|
||||
opts.AddFlag(CmdArgOptShortEquals.BitMask())
|
||||
opts.ClearFlag(CmdArgOptShortNoEquals.BitMask())
|
||||
} else {
|
||||
opts.AddFlag(CmdArgOptShortNoEquals.BitMask())
|
||||
opts.ClearFlag(CmdArgOptShortEquals.BitMask())
|
||||
}
|
||||
if b.HasFlag(CmdArgOptLongNoEquals.BitMask()) && !b.HasFlag(CmdArgOptLongEquals.BitMask()) {
|
||||
opts.AddFlag(CmdArgOptLongNoEquals.BitMask())
|
||||
opts.ClearFlag(CmdArgOptLongEquals.BitMask())
|
||||
} else {
|
||||
opts.AddFlag(CmdArgOptLongEquals.BitMask())
|
||||
opts.ClearFlag(CmdArgOptLongNoEquals.BitMask())
|
||||
}
|
||||
if b.HasFlag(CmdArgOptForcePosix.BitMask()) && !b.HasFlag(CmdArgOptForceNoPosix.BitMask()) {
|
||||
opts.AddFlag(CmdArgOptForcePosix.BitMask())
|
||||
opts.ClearFlag(CmdArgOptForceNoPosix.BitMask())
|
||||
} else {
|
||||
opts.AddFlag(CmdArgOptForceNoPosix.BitMask())
|
||||
opts.ClearFlag(CmdArgOptForcePosix.BitMask())
|
||||
}
|
||||
}
|
||||
fmt.Printf("PARSE AFTER: %d\n", opts.Value())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// tagOptsToMask returns a bitmask.MaskBit from a struct field's tags.
|
||||
func tagOptsToMask(field reflect.StructField) (b *bitmask.MaskBit) {
|
||||
|
||||
var o cmdArgOpt
|
||||
var tagOpts map[string]bool = structutils.TagToBoolMap(field, CmdArgsTag, structutils.TagMapTrim)
|
||||
|
||||
b = bitmask.NewMaskBit()
|
||||
|
||||
// First round, these are normally disabled.
|
||||
for k, v := range tagOpts {
|
||||
switch k {
|
||||
case "prefer_short":
|
||||
o = CmdArgOptPreferShort
|
||||
case "short_equals":
|
||||
o = CmdArgOptShortEquals
|
||||
case "no_long_equals":
|
||||
o = CmdArgOptLongNoEquals
|
||||
case "force_posix":
|
||||
o = CmdArgOptForcePosix
|
||||
}
|
||||
if v {
|
||||
b.AddFlag(o.BitMask())
|
||||
} else {
|
||||
b.ClearFlag(o.BitMask())
|
||||
}
|
||||
}
|
||||
// Second round, these override the above.
|
||||
for k, v := range tagOpts {
|
||||
switch k {
|
||||
case "prefer_long":
|
||||
o = CmdArgOptPreferShort
|
||||
case "no_short_equals":
|
||||
o = CmdArgOptShortEquals
|
||||
case "long_equals":
|
||||
o = CmdArgOptLongNoEquals
|
||||
case "force_no_posix":
|
||||
o = CmdArgOptForcePosix
|
||||
}
|
||||
// Since these are meant to disable, we flip things around.
|
||||
if v {
|
||||
b.ClearFlag(o.BitMask())
|
||||
} else {
|
||||
b.AddFlag(o.BitMask())
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
13
exec_extra/funcs_cmdargopt.go
Normal file
13
exec_extra/funcs_cmdargopt.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package exec_extra
|
||||
|
||||
import (
|
||||
`r00t2.io/goutils/bitmask`
|
||||
)
|
||||
|
||||
// BitMask returns the underlying bitmask.MaskBit representation of a cmdArgOpt.
|
||||
func (c cmdArgOpt) BitMask() (b bitmask.MaskBit) {
|
||||
|
||||
b = bitmask.MaskBit(c)
|
||||
|
||||
return
|
||||
}
|
||||
27
exec_extra/funcs_test.go
Normal file
27
exec_extra/funcs_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package exec_extra
|
||||
|
||||
import (
|
||||
`testing`
|
||||
)
|
||||
|
||||
type (
|
||||
testStruct struct {
|
||||
Foo string `cmdarg:"short=f,long=foo"`
|
||||
Bar int `cmdarg:"short=b,long=bar,prefer_short"`
|
||||
}
|
||||
)
|
||||
|
||||
func TestGetCmdFromStruct(t *testing.T) {
|
||||
|
||||
var err error
|
||||
var out []string
|
||||
var v *testStruct = &testStruct{
|
||||
Foo: "foo",
|
||||
Bar: 123,
|
||||
}
|
||||
|
||||
if out, err = GetCmdFromStruct(v); err != nil {
|
||||
t.Fatalf("Received error getting command from struct: %v", err)
|
||||
}
|
||||
t.Logf("Got command args from struct:\n%#v", out)
|
||||
}
|
||||
@@ -1,9 +1,25 @@
|
||||
package exec_extra
|
||||
|
||||
type cmdArgsOpts struct {
|
||||
preferShort bool
|
||||
forcePosix bool
|
||||
cmd *string
|
||||
}
|
||||
import (
|
||||
`reflect`
|
||||
|
||||
type cmdArgOpt func(*cmdArgsOpts) (err error)
|
||||
`r00t2.io/goutils/bitmask`
|
||||
)
|
||||
|
||||
type (
|
||||
cmdArgOpt bitmask.MaskBit
|
||||
)
|
||||
|
||||
type (
|
||||
cmdArgFlag struct {
|
||||
defaults *bitmask.MaskBit
|
||||
fieldOpts *bitmask.MaskBit
|
||||
boolMap map[string]bool
|
||||
strMap map[string]string
|
||||
shortFlag string
|
||||
longFlag string
|
||||
field *reflect.StructField
|
||||
value *reflect.Value
|
||||
argSlice *[]string
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
package exec_extra
|
||||
|
||||
import (
|
||||
`r00t2.io/sysutils/paths`
|
||||
)
|
||||
|
||||
/*
|
||||
CmdArgsWithBin returns a cmdArgsOpt that specifies program/executable/binary path `bin`,
|
||||
ensuring that the resulting cmdSlice from GetCmdFromStruct() will return a ready-to-use slice.
|
||||
(Otherwise the executable would need to be prepended to the resulting slice.)
|
||||
|
||||
Path normalization/canonziation can be enabled/disabled via normalizePath.
|
||||
*/
|
||||
func CmdArgsWithBin(bin string, normalizePath bool) (opt cmdArgOpt, err error) {
|
||||
|
||||
if normalizePath {
|
||||
if err = paths.RealPath(&bin); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
opt = func(opts *cmdArgsOpts) (err error) {
|
||||
/*
|
||||
if opts.cmd == nil {
|
||||
opts.cmd = new(string)
|
||||
}
|
||||
*/
|
||||
*opts.cmd = bin
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user