LEFT | RIGHT |
(no file at all) | |
1 // Copyright 2012, 2013 Canonical Ltd. | 1 // Copyright 2012, 2013 Canonical Ltd. |
2 // Licensed under the AGPLv3, see LICENCE file for details. | 2 // Licensed under the AGPLv3, see LICENCE file for details. |
3 | 3 |
4 package cmd | 4 package cmd |
5 | 5 |
6 import ( | 6 import ( |
7 "bytes" | 7 "bytes" |
8 "errors" | 8 "errors" |
9 "fmt" | 9 "fmt" |
10 "io" | 10 "io" |
(...skipping 16 matching lines...) Expand all Loading... |
27 | 27 |
28 // SetFlags adds command specific flags to the flag set. | 28 // SetFlags adds command specific flags to the flag set. |
29 SetFlags(f *gnuflag.FlagSet) | 29 SetFlags(f *gnuflag.FlagSet) |
30 | 30 |
31 // Init initializes the Command before running. | 31 // Init initializes the Command before running. |
32 Init(args []string) error | 32 Init(args []string) error |
33 | 33 |
34 // Run will execute the Command as directed by the options and positiona
l | 34 // Run will execute the Command as directed by the options and positiona
l |
35 // arguments passed to Init. | 35 // arguments passed to Init. |
36 Run(ctx *Context) error | 36 Run(ctx *Context) error |
| 37 |
| 38 // AllowInterspersedFlags returns whether the command allows flag |
| 39 // arguments to be interspersed with non-flag arguments. |
| 40 AllowInterspersedFlags() bool |
37 } | 41 } |
38 | 42 |
39 // CommandBase provides the default implementation for SetFlags, Init, and Help. | 43 // CommandBase provides the default implementation for SetFlags, Init, and Help. |
40 type CommandBase struct{} | 44 type CommandBase struct{} |
41 | 45 |
42 // SetFlags does nothing in the simplest case. | 46 // SetFlags does nothing in the simplest case. |
43 func (c *CommandBase) SetFlags(f *gnuflag.FlagSet) {} | 47 func (c *CommandBase) SetFlags(f *gnuflag.FlagSet) {} |
44 | 48 |
45 // Init in the simplest case makes sure there are no args. | 49 // Init in the simplest case makes sure there are no args. |
46 func (c *CommandBase) Init(args []string) error { | 50 func (c *CommandBase) Init(args []string) error { |
47 return CheckEmpty(args) | 51 return CheckEmpty(args) |
| 52 } |
| 53 |
| 54 // AllowInterspersedFlags returns true by default. Some subcommands |
| 55 // may want to override this. |
| 56 func (c *CommandBase) AllowInterspersedFlags() bool { |
| 57 return true |
48 } | 58 } |
49 | 59 |
50 // Context represents the run context of a Command. Command implementations | 60 // Context represents the run context of a Command. Command implementations |
51 // should interpret file names relative to Dir (see AbsPath below), and print | 61 // should interpret file names relative to Dir (see AbsPath below), and print |
52 // output and errors to Stdout and Stderr respectively. | 62 // output and errors to Stdout and Stderr respectively. |
53 type Context struct { | 63 type Context struct { |
54 Dir string | 64 Dir string |
55 Stdin io.Reader | 65 Stdin io.Reader |
56 Stdout io.Writer | 66 Stdout io.Writer |
57 Stderr io.Writer | 67 Stderr io.Writer |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
109 f.SetOutput(ioutil.Discard) | 119 f.SetOutput(ioutil.Discard) |
110 if i.Doc != "" { | 120 if i.Doc != "" { |
111 fmt.Fprintf(buf, "\n%s\n", strings.TrimSpace(i.Doc)) | 121 fmt.Fprintf(buf, "\n%s\n", strings.TrimSpace(i.Doc)) |
112 } | 122 } |
113 if len(i.Aliases) > 0 { | 123 if len(i.Aliases) > 0 { |
114 fmt.Fprintf(buf, "\naliases: %s\n", strings.Join(i.Aliases, ", "
)) | 124 fmt.Fprintf(buf, "\naliases: %s\n", strings.Join(i.Aliases, ", "
)) |
115 } | 125 } |
116 return buf.Bytes() | 126 return buf.Bytes() |
117 } | 127 } |
118 | 128 |
119 // ParseArgs encapsulate the parsing of the args so this function can be | |
120 // called from the testing module too. | |
121 func ParseArgs(c Command, f *gnuflag.FlagSet, args []string) error { | |
122 // If the command is a SuperCommand, we want to parse the args with | |
123 // allowIntersperse=false (i.e. the first parameter to Parse. This will | |
124 // mean that the args may contain other options that haven't been define
d | |
125 // yet, and that only options that relate to the SuperCommand itself can | |
126 // come prior to the subcommand name. | |
127 _, isSuperCommand := c.(*SuperCommand) | |
128 return f.Parse(!isSuperCommand, args) | |
129 } | |
130 | |
131 // Errors from commands can be either ErrHelp, which means "show the help" or | 129 // Errors from commands can be either ErrHelp, which means "show the help" or |
132 // some other error related to needed flags missing, or needed positional args | 130 // some other error related to needed flags missing, or needed positional args |
133 // missing, in which case we should print the error and return a non-zero | 131 // missing, in which case we should print the error and return a non-zero |
134 // return code. | 132 // return code. |
135 func handleCommandError(c Command, ctx *Context, err error, f *gnuflag.FlagSet)
(int, bool) { | 133 func handleCommandError(c Command, ctx *Context, err error, f *gnuflag.FlagSet)
(int, bool) { |
136 if err == gnuflag.ErrHelp { | 134 if err == gnuflag.ErrHelp { |
137 ctx.Stdout.Write(c.Info().Help(f)) | 135 ctx.Stdout.Write(c.Info().Help(f)) |
138 return 0, true | 136 return 0, true |
139 } | 137 } |
140 if err != nil { | 138 if err != nil { |
141 fmt.Fprintf(ctx.Stderr, "error: %v\n", err) | 139 fmt.Fprintf(ctx.Stderr, "error: %v\n", err) |
142 return 2, true | 140 return 2, true |
143 } | 141 } |
144 return 0, false | 142 return 0, false |
145 } | 143 } |
146 | 144 |
147 // Main runs the given Command in the supplied Context with the given | 145 // Main runs the given Command in the supplied Context with the given |
148 // arguments, which should not include the command name. It returns a code | 146 // arguments, which should not include the command name. It returns a code |
149 // suitable for passing to os.Exit. | 147 // suitable for passing to os.Exit. |
150 func Main(c Command, ctx *Context, args []string) int { | 148 func Main(c Command, ctx *Context, args []string) int { |
151 f := gnuflag.NewFlagSet(c.Info().Name, gnuflag.ContinueOnError) | 149 f := gnuflag.NewFlagSet(c.Info().Name, gnuflag.ContinueOnError) |
152 f.SetOutput(ioutil.Discard) | 150 f.SetOutput(ioutil.Discard) |
153 c.SetFlags(f) | 151 c.SetFlags(f) |
154 » if rc, done := handleCommandError(c, ctx, ParseArgs(c, f, args), f); don
e { | 152 » if rc, done := handleCommandError(c, ctx, f.Parse(c.AllowInterspersedFla
gs(), args), f); done { |
155 return rc | 153 return rc |
156 } | 154 } |
157 // Since SuperCommands can also return gnuflag.ErrHelp errors, we need t
o | 155 // Since SuperCommands can also return gnuflag.ErrHelp errors, we need t
o |
158 // handle both those types of errors as well as "real" errors. | 156 // handle both those types of errors as well as "real" errors. |
159 if rc, done := handleCommandError(c, ctx, c.Init(f.Args()), f); done { | 157 if rc, done := handleCommandError(c, ctx, c.Init(f.Args()), f); done { |
160 return rc | 158 return rc |
161 } | 159 } |
162 if err := c.Run(ctx); err != nil { | 160 if err := c.Run(ctx); err != nil { |
163 if err != ErrSilent { | 161 if err != ErrSilent { |
164 fmt.Fprintf(ctx.Stderr, "error: %v\n", err) | 162 fmt.Fprintf(ctx.Stderr, "error: %v\n", err) |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
199 func ZeroOrOneArgs(args []string) (string, error) { | 197 func ZeroOrOneArgs(args []string) (string, error) { |
200 var result string | 198 var result string |
201 if len(args) > 0 { | 199 if len(args) > 0 { |
202 result, args = args[0], args[1:] | 200 result, args = args[0], args[1:] |
203 } | 201 } |
204 if err := CheckEmpty(args); err != nil { | 202 if err := CheckEmpty(args); err != nil { |
205 return "", err | 203 return "", err |
206 } | 204 } |
207 return result, nil | 205 return result, nil |
208 } | 206 } |
LEFT | RIGHT |