File: commands.go

package info (click to toggle)
golang-github-traefik-paerser 0.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 612 kB
  • sloc: makefile: 14
file content (148 lines) | stat: -rw-r--r-- 3,656 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
// Package cli provides tools to create commands that support advanced configuration features,
// sub-commands, and allowing configuration from command-line flags, configuration files, and environment variables.
package cli

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
)

// Command structure contains program/command information (command name and description).
type Command struct {
	Name           string
	Description    string
	Configuration  interface{}
	Resources      []ResourceLoader
	Run            func([]string) error
	CustomHelpFunc func(io.Writer, *Command) error
	Hidden         bool
	// AllowArg if not set, disallows any argument that is not a known command or a sub-command.
	AllowArg    bool
	subCommands []*Command
}

// AddCommand Adds a sub command.
func (c *Command) AddCommand(cmd *Command) error {
	if c == nil || cmd == nil {
		return nil
	}

	if c.Name == cmd.Name {
		return fmt.Errorf("child command cannot have the same name as their parent: %s", cmd.Name)
	}

	c.subCommands = append(c.subCommands, cmd)
	return nil
}

// PrintHelp calls the custom help function of the command if it's set.
// Otherwise, it calls the default help function.
func (c *Command) PrintHelp(w io.Writer) error {
	if c.CustomHelpFunc != nil {
		return c.CustomHelpFunc(w, c)
	}
	return PrintHelp(w, c)
}

// Execute Executes a command.
func Execute(cmd *Command) error {
	return execute(cmd, os.Args, true)
}

func execute(cmd *Command, args []string, root bool) error {
	// Calls command without args.
	if len(args) == 1 {
		if err := run(cmd, args[1:]); err != nil {
			return fmt.Errorf("command %s error: %w", args[0], err)
		}
		return nil
	}

	// Special case: if the command is the top level one,
	// and the first arg (`args[1]`) is not the command name or a known sub-command,
	// then we run the top level command itself.
	if root && cmd.Name != args[1] && !contains(cmd.subCommands, args[1]) {
		if err := run(cmd, args[1:]); err != nil {
			return fmt.Errorf("command %s error: %w", filepath.Base(args[0]), err)
		}
		return nil
	}

	// Calls command by its name.
	if len(args) >= 2 && cmd.Name == args[1] {
		if len(args) < 3 || !contains(cmd.subCommands, args[2]) {
			if err := run(cmd, args[2:]); err != nil {
				return fmt.Errorf("command %s error: %w", cmd.Name, err)
			}
			return nil
		}
	}

	// No sub-command, calls the current command.
	if len(cmd.subCommands) == 0 {
		if err := run(cmd, args[1:]); err != nil {
			return fmt.Errorf("command %s error: %w", cmd.Name, err)
		}
		return nil
	}

	// Trying to find the sub-command.
	for _, subCmd := range cmd.subCommands {
		if len(args) >= 2 && subCmd.Name == args[1] {
			return execute(subCmd, args, false)
		}
		if len(args) >= 3 && subCmd.Name == args[2] {
			return execute(subCmd, args[1:], false)
		}
	}

	return fmt.Errorf("command not found: %v", args)
}

func run(cmd *Command, args []string) error {
	if len(args) > 0 && !isFlag(args[0]) && !cmd.AllowArg {
		_ = cmd.PrintHelp(os.Stdout)
		return fmt.Errorf("command not found: %s", args[0])
	}

	if isHelp(args) {
		return cmd.PrintHelp(os.Stdout)
	}

	if cmd.Run == nil {
		_ = cmd.PrintHelp(os.Stdout)
		return fmt.Errorf("command %s is not runnable", cmd.Name)
	}

	if cmd.Configuration == nil {
		return cmd.Run(args)
	}

	for _, resource := range cmd.Resources {
		done, err := resource.Load(args, cmd)
		if err != nil {
			return err
		}
		if done {
			break
		}
	}

	return cmd.Run(args)
}

func contains(cmds []*Command, name string) bool {
	for _, cmd := range cmds {
		if cmd.Name == name {
			return true
		}
	}

	return false
}

func isFlag(arg string) bool {
	return len(arg) > 0 && arg[0] == '-'
}