File: cmd.go

package info (click to toggle)
singularity-container 4.1.5%2Bds4-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 43,876 kB
  • sloc: asm: 14,840; sh: 3,190; ansic: 1,751; awk: 414; makefile: 413; python: 99
file content (185 lines) | stat: -rw-r--r-- 5,473 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// Copyright (c) 2019, Sylabs Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE.md file distributed with the sources of this project regarding your
// rights to use or distribute this software.

package cmdline

import (
	"fmt"
	"strings"

	"github.com/spf13/cobra"
)

// CommandManager holds root command or first parent
// can stores group of command. A command group can be
// composed of one or many commands
type CommandManager struct {
	rootCmd   *cobra.Command
	groupCmds map[string][]*cobra.Command
	errPool   []error
	fm        *flagManager
}

// FlagError represents a flag error type
type FlagError string

func (f FlagError) Error() string {
	return string(f)
}

// CommandError represents a command error type
type CommandError string

func (c CommandError) Error() string {
	return string(c)
}

func onError(_ *cobra.Command, err error) error {
	return FlagError(err.Error())
}

// NewCommandManager instantiates a CommandManager.
func NewCommandManager(rootCmd *cobra.Command) *CommandManager {
	if rootCmd == nil {
		panic("nil root command passed")
	}
	cm := &CommandManager{
		rootCmd:   rootCmd,
		groupCmds: make(map[string][]*cobra.Command),
		fm:        newFlagManager(),
	}
	rootCmd.SetFlagErrorFunc(onError)
	return cm
}

func (m *CommandManager) isRegistered(cmd *cobra.Command) bool {
	c := cmd.Parent()
	for c != nil {
		if c == m.rootCmd {
			break
		}
		c = c.Parent()
	}
	return c != nil
}

func (m *CommandManager) pushError(err error) {
	m.errPool = append(m.errPool, err)
}

// GetError returns the error pool.
func (m *CommandManager) GetError() []error {
	return m.errPool
}

// RegisterCmd registers a child command of the root command.
// The registered command is automatically affected to a unique group
// containing this command only, the group name is based on the command name
func (m *CommandManager) RegisterCmd(cmd *cobra.Command) {
	// panic here because it's a misuse of API and generally from
	// global context or init() functions
	if cmd == nil {
		panic("nil command passed")
	}
	cmd.SetFlagErrorFunc(onError)
	m.rootCmd.AddCommand(cmd)
	cmd.Flags().SetInterspersed(false)
	m.SetCmdGroup(m.GetCmdName(cmd), cmd)
}

// RegisterSubCmd registers a child command for parent command given as argument.
// The registered command is automatically affected to a unique group containing
// this command only, the group name is based on the command name appended to
// parents command name (see GetCmdName for details)
func (m *CommandManager) RegisterSubCmd(parentCmd, childCmd *cobra.Command) {
	// panic here because it's a misuse of API and generally from
	// global context or init() functions
	if parentCmd == nil {
		panic("nil parent command passed")
	} else if childCmd == nil {
		panic("nil child command passed")
	} else if !m.isRegistered(parentCmd) {
		panic("parent command not registered")
	}
	parentCmd.AddCommand(childCmd)
	childCmd.Flags().SetInterspersed(false)
	m.SetCmdGroup(m.GetCmdName(childCmd), childCmd)
}

// SetCmdGroup creates a unique group of commands identified by name.
// If group already exists or empty command is passed, this function
// will panic
func (m *CommandManager) SetCmdGroup(name string, cmds ...*cobra.Command) {
	if m.groupCmds[name] != nil {
		panic(fmt.Sprintf("group %s already exists", name))
	}
	tmp := make([]*cobra.Command, 0, len(cmds))
	for _, c := range cmds {
		if c != nil {
			tmp = append(tmp, c)
		}
	}
	// cmds could contain only nil commands, we check length of
	// the temporary allocated array containing only non nil
	// commands
	if len(tmp) == 0 {
		panic(fmt.Sprintf("creation of an empty group %q", name))
	}
	m.groupCmds[name] = tmp
}

// GetRootCmd returns the root command
func (m *CommandManager) GetRootCmd() *cobra.Command {
	return m.rootCmd
}

// GetCmdGroup returns all commands associated with the group name
func (m *CommandManager) GetCmdGroup(name string) []*cobra.Command {
	return m.groupCmds[name]
}

// GetCmd returns a single command based on its unique group name.
// If the command group has more than one command this function
// return a nil command instead.
func (m *CommandManager) GetCmd(name string) *cobra.Command {
	cmds := m.groupCmds[name]
	if cmds == nil || len(cmds) > 1 {
		return nil
	}
	return cmds[0]
}

// GetCmdName returns name associated with the provided command.
// If command is named "child" and has two parents named "parent1"
// and "parent2", this function will return "parent1_parent2_child".
// Passing the root command to this function returns an empty string.
func (m *CommandManager) GetCmdName(cmd *cobra.Command) string {
	var names []string

	for c := cmd; c != nil; c = c.Parent() {
		if c == m.rootCmd {
			break
		}
		names = append(names, c.Name())
	}
	// reverse slice
	for i, j := 0, len(names)-1; i < j; i, j = i+1, j-1 {
		names[i], names[j] = names[j], names[i]
	}
	return strings.Join(names, "_")
}

// RegisterFlagForCmd registers a flag for one or many commands
func (m *CommandManager) RegisterFlagForCmd(flag *Flag, cmds ...*cobra.Command) {
	if err := m.fm.registerFlagForCmd(flag, cmds...); err != nil {
		m.pushError(err)
	}
}

// UpdateCmdFlagFromEnv updates flag's values based on environment variables
// associated with all flags belonging to command provided as argument
func (m *CommandManager) UpdateCmdFlagFromEnv(cmd *cobra.Command, envPrefix string) error {
	return m.fm.updateCmdFlagFromEnv(cmd, envPrefix)
}