diff --git a/Gopkg.lock b/Gopkg.lock index 22d40dc867..8c2d15daa6 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1171,11 +1171,11 @@ [[projects]] branch = "master" - digest = "1:c69a05ea10fc186aaf589c10184d00dbfa3a46fbd7866ed1e52d6bd84f8607d5" + digest = "1:b62e87e9e21e5d6933164761e3f1861c580d0612fcde43cc8c6b2e4db34ce9f8" name = "yunion.io/x/structarg" packages = ["."] pruneopts = "UT" - revision = "7dc6c41bf325ce7f2ce2a1cebbd8d396f7f6188e" + revision = "5a0eff15d64f686ee66801ac42284a797115e55f" [solve-meta] analyzer-name = "dep" diff --git a/cmd/climc/climc.go b/cmd/climc/climc.go index f6beed408b..bc9618729f 100644 --- a/cmd/climc/climc.go +++ b/cmd/climc/climc.go @@ -76,15 +76,16 @@ func getSubcommandsParser() (*structarg.ArgumentParser, error) { cmd := v.Command for _, v := range _par.GetOptArgs() { - _name := strings.Replace(v.String(), "]", "", -1) - _name = strings.Replace(_name, "[", "", -1) - promputils.AppendOpt(cmd, _name, v.HelpString("")) - + text := v.String() + text = strings.TrimLeft(text, "[<") + text = strings.TrimRight(text, "]>") + promputils.AppendOpt(cmd, text, v.HelpString("")) } for _, v := range _par.GetPosArgs() { - _name := strings.Replace(v.String(), "<", "", -1) - _name = strings.Replace(_name, ">", "", -1) - promputils.AppendPos(cmd, _name, v.HelpString("")) + text := v.String() + text = strings.TrimLeft(text, "[<") + text = strings.TrimRight(text, "]>") + promputils.AppendPos(cmd, text, v.HelpString("")) } } return parse, nil diff --git a/pkg/cloudcommon/validators/validators.go b/pkg/cloudcommon/validators/validators.go index 305beb3a2c..0b99c0e977 100644 --- a/pkg/cloudcommon/validators/validators.go +++ b/pkg/cloudcommon/validators/validators.go @@ -11,6 +11,7 @@ import ( "net" "reflect" "regexp" + "strings" "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/cloudcommon/db" @@ -60,6 +61,11 @@ func (v *Validator) setDefault(data *jsonutils.JSONDict) bool { v.value = jsonutils.NewString(s) data.Set(v.Key, v.value) return true + case bool: + b := v.defaultVal.(bool) + v.value = jsonutils.NewBool(b) + data.Set(v.Key, v.value) + return true case int, int32, int64, uint, uint32, uint64: value := reflect.ValueOf(v.defaultVal) value64 := value.Convert(gotypes.Int64Type) @@ -141,6 +147,125 @@ func (v *ValidatorStringChoices) Validate(data *jsonutils.JSONDict) error { return nil } +type ValidatorStringMultiChoices struct { + Validator + Choices Choices + defaultVal string + Value string + sep string + keepDup bool +} + +func NewStringMultiChoicesValidator(key string, choices Choices) *ValidatorStringMultiChoices { + v := &ValidatorStringMultiChoices{ + Validator: Validator{Key: key}, + Choices: choices, + } + v.parent = v + return v +} + +func (v *ValidatorStringMultiChoices) Sep(s string) *ValidatorStringMultiChoices { + v.sep = s + return v +} + +func (v *ValidatorStringMultiChoices) KeepDup(b bool) *ValidatorStringMultiChoices { + v.keepDup = b + return v +} + +func (v *ValidatorStringMultiChoices) validateString(s string) (string, bool) { + choices := strings.Split(s, v.sep) + j := 0 + for i, choice := range choices { + if !v.Choices.Has(choice) { + return "", false + } + if !v.keepDup { + isDup := false + for k := 0; k < j; k++ { + if choices[k] == choices[i] { + isDup = true + } + } + if !isDup { + choices[j] = choices[i] + j += 1 + } + } + } + if !v.keepDup { + choices = choices[:j] + } + s = strings.Join(choices, v.sep) + return s, true +} + +func (v *ValidatorStringMultiChoices) Default(s string) IValidator { + s, ok := v.validateString(s) + if !ok { + panic("invalid default for " + v.Key) + } + v.Validator.Default(s) + return v +} + +func (v *ValidatorStringMultiChoices) getValue() interface{} { + return v.Value +} + +func (v *ValidatorStringMultiChoices) Validate(data *jsonutils.JSONDict) error { + if err, isSet := v.Validator.validateEx(data); err != nil || !isSet { + return err + } + s, err := v.value.GetString() + if err != nil { + return newGeneralError(v.Key, err) + } + s, ok := v.validateString(s) + if !ok { + return newInvalidChoiceError(v.Key, v.Choices, s) + } + // in case it's stringified from v.value + data.Set(v.Key, jsonutils.NewString(s)) + v.Value = s + return nil +} + +type ValidatorBool struct { + Validator + Value bool +} + +func (v *ValidatorBool) getValue() interface{} { + return v.Value +} + +func (v *ValidatorBool) Default(i bool) IValidator { + return v.Validator.Default(i) +} +func (v *ValidatorBool) Validate(data *jsonutils.JSONDict) error { + if err, isSet := v.Validator.validateEx(data); err != nil || !isSet { + return err + } + i, err := v.value.Bool() + if err != nil { + return newInvalidTypeError(v.Key, "bool", err) + } + data.Set(v.Key, jsonutils.NewBool(i)) + v.Value = i + return nil +} + +func NewBoolValidator(key string) *ValidatorBool { + v := &ValidatorBool{ + Validator: Validator{Key: key}, + } + v.parent = v + return v +} + type ValidatorRange struct { Validator Lower int64 diff --git a/pkg/cloudcommon/validators/validators_test.go b/pkg/cloudcommon/validators/validators_test.go index 1323554b5b..29f110a2bc 100644 --- a/pkg/cloudcommon/validators/validators_test.go +++ b/pkg/cloudcommon/validators/validators_test.go @@ -143,6 +143,88 @@ func TestStringChoicesValidator(t *testing.T) { } } +func TestBoolValidator(t *testing.T) { + cases := []*C{ + { + Name: "missing non-optional", + In: `{}`, + Out: `{}`, + Err: ERR_MISSING_KEY, + ValueWant: false, + }, + { + Name: "missing optional", + In: `{}`, + Out: `{}`, + Optional: true, + ValueWant: false, + }, + { + Name: "missing with default", + In: `{}`, + Out: `{s: true}`, + Default: true, + ValueWant: true, + }, + { + Name: "true", + In: `{s: true}`, + Out: `{s: true}`, + ValueWant: true, + }, + { + Name: "false", + In: `{s: false}`, + Out: `{s: false}`, + ValueWant: false, + }, + { + Name: `parsed "true"`, + In: `{s: "true"}`, + Out: `{s: true}`, + ValueWant: true, + }, + { + Name: `parsed "on"`, + In: `{s: "on"}`, + Out: `{s: true}`, + ValueWant: true, + }, + { + Name: `parsed "yes"`, + In: `{s: "yes"}`, + Out: `{s: true}`, + ValueWant: true, + }, + { + Name: `parsed "1"`, + In: `{s: "1"}`, + Out: `{s: true}`, + ValueWant: true, + }, + { + Name: "parsed invalid", + In: `{s: "abc"}`, + Out: `{s: "abc"}`, + Err: ERR_INVALID_TYPE, + ValueWant: false, + }, + } + for _, c := range cases { + t.Run(c.Name, func(t *testing.T) { + v := NewBoolValidator("s") + if c.Default != nil { + i := c.Default.(bool) + v.Default(i) + } + if c.Optional { + v.Optional(true) + } + testS(t, v, c) + }) + } +} + func TestRangeValidator(t *testing.T) { cases := []*C{ { diff --git a/vendor/yunion.io/x/structarg/structarg.go b/vendor/yunion.io/x/structarg/structarg.go index 5ff1cd22d4..0dad9de69a 100644 --- a/vendor/yunion.io/x/structarg/structarg.go +++ b/vendor/yunion.io/x/structarg/structarg.go @@ -459,12 +459,7 @@ func (this *SingleArgument) MetaVar() string { if len(this.metavar) > 0 { return this.metavar } else if len(this.choices) > 0 { - choices := this.choices - if len(choices) > 2 { - choices = choices[:2] - choices = append(choices, "...") - } - return fmt.Sprintf("{%s}", strings.Join(choices, ",")) + return fmt.Sprintf("{%s}", strings.Join(this.choices, ",")) } else { return strings.ToUpper(strings.Replace(this.Token(), "-", "_", -1)) } @@ -561,7 +556,7 @@ func (this *SingleArgument) SetValue(val string) error { for i := 0; i < len(cands); i += 1 { cands[i] = fmt.Sprintf("'%s'", cands[i]) } - msg = fmt.Sprintf("%s, do you mean %s?", msg, ChoicesString(cands)) + msg = fmt.Sprintf("%s, did you mean %s?", msg, ChoicesString(cands)) } return fmt.Errorf(msg) }