cli/flag.go (195 lines of code) (raw):

// Copyright (c) 2009-present, Alibaba Cloud All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cli import ( "fmt" "strconv" "github.com/aliyun/aliyun-cli/v3/i18n" ) type AssignedMode int const ( AssignedNone = AssignedMode(-1) AssignedDefault = AssignedMode(0) AssignedOnce = AssignedMode(1) AssignedRepeatable = AssignedMode(9) ) type Flag struct { // Name of flag --{Name} Name string // Flag is the single characters Shorthand rune // Message print with --help command Short *i18n.Text // Message print with `help --flag` command Long *i18n.Text // If DefaultValue is not "" and Required is true, if flag is not assign // GetValue() will return DefaultValue, and IsAssigned() will be false DefaultValue string // If Required is true, the flag must be assigned with `--flag value` or DefaultValue is not empty Required bool // Enable flag has alias names Aliases []string // Ref to AssignedMode // `AssignedNone`: flag only appear with `--flag1` `--flag2` // `AssignedDefault`: flag can appear with `--flag1` or `--flag1 value1` // `AssignedOnce`: flag only appear with `--flag1 value1` // `AssignedRepeatable`: flag can appear multi times sample: `--flag1 [v1] [v2] [v3] [field1=value1]`, use with Fields AssignedMode AssignedMode // If Persistent is true, the flag can apply to child commands Persistent bool // If Hidden is true, it will not appear in --help mode Hidden bool // Using in FlagSet.GetByCategory()... Category string // Use to validate flag is in correct format Validate func(f *Flag) error // Flag can assigned with --flag field1=value1 field2=value2 value3 ... // must used with AssignedMode=AssignedRepeatable Fields []Field // Flag can't appear with other flags, use Flag.Name ExcludeWith []string assigned bool value string values []string // formation string } // return true if flag appeared, either `--flag1` or `--flag1 value1` func (f *Flag) IsAssigned() bool { if f == nil { return false } return f.assigned } func (f *Flag) SetAssigned(istrue bool) { f.assigned = istrue } func (f *Flag) SetValue(value string) { f.value = value } // return flag value, if not assigned return f.DefaultValue // // for `AssignedMode == AssignedRepeatable`. Use GetValues() to get all values func (f *Flag) GetValue() (string, bool) { if f == nil { return "", false } if f.IsAssigned() { return f.value, true } else if f.Required { return f.DefaultValue, false } else { return "", false } } // for `AssignedMode == AssignedRepeatable` flag, return values func (f *Flag) GetValues() []string { return f.values } func (f *Flag) SetValues(values []string) { f.values = values } // for `AssignedMode == AssignedRepeatable` flag, return fields, multiply assignable // Sample: --output abc bbc acd bb=2 cc=3 func (f *Flag) getField(key string) (*Field, bool) { for i, field := range f.Fields { if field.Key == key { return &(f.Fields[i]), true } } return nil, false } // --flag field1=value1 func (f *Flag) GetFieldValue(key string) (string, bool) { if field, ok := f.getField(key); ok { return field.getValue() } return "", false } func (f *Flag) GetFieldValues(key string) []string { if field, ok := f.getField(key); ok { return field.values } return make([]string, 0) } // return def if Flag is not assigned func (f *Flag) GetStringOrDefault(def string) string { if f == nil { return def } if f.assigned { return f.value } return def } // TODO: flag support integer validate // return def if Flag is not assign or assign failed func (f *Flag) GetIntegerOrDefault(def int) int { if f == nil { return def } if f.assigned { if i, err := strconv.Atoi(f.value); err == nil { return i } } return def } // get all appears forms, maybe {"--Name", "--Alias1", "-Shorthand"} func (f *Flag) GetFormations() []string { r := make([]string, 0) if f.Name != "" { r = append(r, "--"+f.Name) } for _, s := range f.Aliases { r = append(r, "--"+s) } if f.Shorthand != 0 { r = append(r, "-"+string(f.Shorthand)) } return r } // if this flag is appeared set assigned = true func (f *Flag) setIsAssigned() error { if !f.assigned { f.assigned = true } else { if f.AssignedMode != AssignedRepeatable { return fmt.Errorf("--%s duplicated", f.Name) } } return nil } // return true, if this flag need assigned with values func (f *Flag) needValue() bool { switch f.AssignedMode { case AssignedNone: return false case AssignedDefault: return f.value == "" case AssignedOnce: return f.value == "" case AssignedRepeatable: return true default: panic(fmt.Errorf("unexpected Flag.AssignedMode %s", strconv.Itoa(int(f.AssignedMode)))) } } // make check valid func (f *Flag) checkValid() { if len(f.Fields) > 0 { if f.AssignedMode != AssignedRepeatable { panic(fmt.Errorf("flag %s with fields must use AssignedRepeatable", f.Name)) } } } // validate flag value func (f *Flag) validate() error { if f.AssignedMode == AssignedOnce && f.value == "" { return fmt.Errorf("--%s must be assigned with value", f.Name) } return nil } // assign value func (f *Flag) assign(v string) error { if f.AssignedMode == AssignedNone { return fmt.Errorf("flag --%s can't be assiged", f.Name) } f.assigned = true f.value = v if f.AssignedMode == AssignedRepeatable { f.values = append(f.values, v) if len(f.Fields) > 0 { f.assignField(v) } } return nil } // assign field func (f *Flag) assignField(s string) error { if k, v, ok := SplitStringWithPrefix(s, "="); ok { field, ok2 := f.getField(k) if ok2 { field.assign(v) } else { return fmt.Errorf("--%s can't assign with %s=", f.Name, k) } } else { field, ok2 := f.getField("") if ok2 { field.assign(v) } else { return fmt.Errorf("--%s can't assign with value", f.Name) } } return nil } func (f *Flag) checkFields() error { if len(f.Fields) == 0 { return nil } for _, field := range f.Fields { if err := field.check(); err != nil { return fmt.Errorf("bad flag format --%s with field %s", f.Name, err) } } return nil }