// Copyright 2013-2023 The Cobra Authors//// 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 cobraimport (flag)const ( requiredAsGroupAnnotation = "cobra_annotation_required_if_others_set" oneRequiredAnnotation = "cobra_annotation_one_required" mutuallyExclusiveAnnotation = "cobra_annotation_mutually_exclusive")// MarkFlagsRequiredTogether marks the given flags with annotations so that Cobra errors// if the command is invoked with a subset (but not all) of the given flags.func ( *Command) ( ...string) { .mergePersistentFlags()for , := range { := .Flags().Lookup()if == nil {panic(fmt.Sprintf("Failed to find flag %q and mark it as being required in a flag group", )) }if := .Flags().SetAnnotation(, requiredAsGroupAnnotation, append(.Annotations[requiredAsGroupAnnotation], strings.Join(, " "))); != nil {// Only errs if the flag isn't found.panic() } }}// MarkFlagsOneRequired marks the given flags with annotations so that Cobra errors// if the command is invoked without at least one flag from the given set of flags.func ( *Command) ( ...string) { .mergePersistentFlags()for , := range { := .Flags().Lookup()if == nil {panic(fmt.Sprintf("Failed to find flag %q and mark it as being in a one-required flag group", )) }if := .Flags().SetAnnotation(, oneRequiredAnnotation, append(.Annotations[oneRequiredAnnotation], strings.Join(, " "))); != nil {// Only errs if the flag isn't found.panic() } }}// MarkFlagsMutuallyExclusive marks the given flags with annotations so that Cobra errors// if the command is invoked with more than one flag from the given set of flags.func ( *Command) ( ...string) { .mergePersistentFlags()for , := range { := .Flags().Lookup()if == nil {panic(fmt.Sprintf("Failed to find flag %q and mark it as being in a mutually exclusive flag group", )) }// Each time this is called is a single new entry; this allows it to be a member of multiple groups if needed.if := .Flags().SetAnnotation(, mutuallyExclusiveAnnotation, append(.Annotations[mutuallyExclusiveAnnotation], strings.Join(, " "))); != nil {panic() } }}// ValidateFlagGroups validates the mutuallyExclusive/oneRequired/requiredAsGroup logic and returns the// first error encountered.func ( *Command) () error {if .DisableFlagParsing {returnnil } := .Flags()// groupStatus format is the list of flags as a unique ID, // then a map of each flag name and whether it is set or not. := map[string]map[string]bool{} := map[string]map[string]bool{} := map[string]map[string]bool{} .VisitAll(func( *flag.Flag) {processFlagForGroupAnnotation(, , requiredAsGroupAnnotation, )processFlagForGroupAnnotation(, , oneRequiredAnnotation, )processFlagForGroupAnnotation(, , mutuallyExclusiveAnnotation, ) })if := validateRequiredFlagGroups(); != nil {return }if := validateOneRequiredFlagGroups(); != nil {return }if := validateExclusiveFlagGroups(); != nil {return }returnnil}func hasAllFlags( *flag.FlagSet, ...string) bool {for , := range { := .Lookup()if == nil {returnfalse } }returntrue}func processFlagForGroupAnnotation( *flag.FlagSet, *flag.Flag, string, map[string]map[string]bool) { , := .Annotations[]if {for , := range {if [] == nil { := strings.Split(, " ")// Only consider this flag group at all if all the flags are defined.if !hasAllFlags(, ...) {continue } [] = make(map[string]bool, len())for , := range { [][] = false } } [][.Name] = .Changed } }}func validateRequiredFlagGroups( map[string]map[string]bool) error { := sortedKeys()for , := range { := [] := []string{}for , := range {if ! { = append(, ) } }iflen() == len() || len() == 0 {continue }// Sort values, so they can be tested/scripted against consistently.sort.Strings()returnfmt.Errorf("if any flags in the group [%v] are set they must all be set; missing %v", , ) }returnnil}func validateOneRequiredFlagGroups( map[string]map[string]bool) error { := sortedKeys()for , := range { := []var []stringfor , := range {if { = append(, ) } }iflen() >= 1 {continue }// Sort values, so they can be tested/scripted against consistently.sort.Strings()returnfmt.Errorf("at least one of the flags in the group [%v] is required", ) }returnnil}func validateExclusiveFlagGroups( map[string]map[string]bool) error { := sortedKeys()for , := range { := []var []stringfor , := range {if { = append(, ) } }iflen() == 0 || len() == 1 {continue }// Sort values, so they can be tested/scripted against consistently.sort.Strings()returnfmt.Errorf("if any flags in the group [%v] are set none of the others can be; %v were all set", , ) }returnnil}func sortedKeys( map[string]map[string]bool) []string { := make([]string, len()) := 0for := range { [] = ++ }sort.Strings()return}// enforceFlagGroupsForCompletion will do the following:// - when a flag in a group is present, other flags in the group will be marked required// - when none of the flags in a one-required group are present, all flags in the group will be marked required// - when a flag in a mutually exclusive group is present, other flags in the group will be marked as hidden// This allows the standard completion logic to behave appropriately for flag groupsfunc ( *Command) () {if .DisableFlagParsing {return } := .Flags() := map[string]map[string]bool{} := map[string]map[string]bool{} := map[string]map[string]bool{} .Flags().VisitAll(func( *flag.Flag) {processFlagForGroupAnnotation(, , requiredAsGroupAnnotation, )processFlagForGroupAnnotation(, , oneRequiredAnnotation, )processFlagForGroupAnnotation(, , mutuallyExclusiveAnnotation, ) })// If a flag that is part of a group is present, we make all the other flags // of that group required so that the shell completion suggests them automaticallyfor , := range {for , := range {if {// One of the flags of the group is set, mark the other ones as requiredfor , := rangestrings.Split(, " ") { _ = .MarkFlagRequired() } } } }// If none of the flags of a one-required group are present, we make all the flags // of that group required so that the shell completion suggests them automaticallyfor , := range { := falsefor _, = range {if {break } }// None of the flags of the group are set, mark all flags in the group // as requiredif ! {for , := rangestrings.Split(, " ") { _ = .MarkFlagRequired() } } }// If a flag that is mutually exclusive to others is present, we hide the other // flags of that group so the shell completion does not suggest themfor , := range {for , := range {if {// One of the flags of the mutually exclusive group is set, mark the other ones as hidden // Don't mark the flag that is already set as hidden because it may be an // array or slice flag and therefore must continue being suggestedfor , := rangestrings.Split(, " ") {if != { := .Flags().Lookup() .Hidden = true } } } } }}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.