Skip to main content
Version: 2.8.x(Latest)

Pain Points in Command Line Management

Previously, we introduced command line management by obtaining parsed parameters and option data through the parser object of the callback function. The following pain points exist when using it:

  • Need to manually pass hard-coded parameter index or option name information to obtain data.
  • Difficult to define descriptions and introductions for parameters/options.
  • Difficult to define data types for parameters/options.
  • Difficult to perform general data validation on parameters/options.
  • It is a disaster for projects that need to manage a large number of command lines.

Object-Oriented Command Management

Let's take a simple example of structured parameter management. We transform the previously introduced Command example into structured management:

package main

import (
"context"
"fmt"

"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/gctx"
)

type cMain struct {
g.Meta `name:"main"`
}

type cMainHttpInput struct {
g.Meta `name:"http" brief:"start http server"`
}
type cMainHttpOutput struct{}

type cMainGrpcInput struct {
g.Meta `name:"grpc" brief:"start grpc server"`
}
type cMainGrpcOutput struct{}

func (c *cMain) Http(ctx context.Context, in cMainHttpInput) (out *cMainHttpOutput, err error) {
fmt.Println("start http server")
return
}

func (c *cMain) Grpc(ctx context.Context, in cMainGrpcInput) (out *cMainGrpcOutput, err error) {
fmt.Println("start grpc server")
return
}

func main() {
cmd, err := gcmd.NewFromObject(cMain{})
if err != nil {
panic(err)
}
cmd.Run(gctx.New())
}

As you can see, we manage the parent command in the form of an object, manage its subcommands in the form of methods, and define the description/parameters/options of subcommands through standardized Input input parameter objects. In most scenarios, you can ignore the use of the Output return object, but for standardization and extensibility, it must be retained. If not used, just return nil for this return parameter. The struct tags used will be introduced later.

We compile the example code and run it to see the effect:

$ main
USAGE
main COMMAND [OPTION]

COMMAND
http start http server
grpc start grpc server

DESCRIPTION
this is the command entry for starting your process

Using the http command:

$ main http
start http server

Using the grpc command:

$ main grpc
start grpc server

The effect is consistent with the previously introduced example.

Structured Parameter Management

Since the command line is managed through objects, let's carefully look at how parameters/options are managed through structure.

We simplify the above instance a bit for a simple example of starting an http service through the http command:

package main

import (
"context"

"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/gctx"
)

type cMain struct {
g.Meta `name:"main" brief:"start http server"`
}

type cMainHttpInput struct {
g.Meta `name:"http" brief:"start http server"`
Name string `v:"required" name:"NAME" arg:"true" brief:"server name"`
Port int `v:"required" short:"p" name:"port" brief:"port of http server"`
}
type cMainHttpOutput struct{}

func (c *cMain) Http(ctx context.Context, in cMainHttpInput) (out *cMainHttpOutput, err error) {
s := g.Server(in.Name)
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Write("Hello world")
})
s.SetPort(in.Port)
s.Run()
return
}

func main() {
cmd, err := gcmd.NewFromObject(cMain{})
if err != nil {
panic(err)
}
cmd.Run(gctx.New())
}

We defined two input parameters for the http command:

  • NAME The name of the service, entered through a parameter. The uppercase form is used here for easy display in the automatically generated help information.
  • port The port of the service, entered through the p/port option.

We also use the v:"required" validation tag to bind mandatory validation rules for these two parameters. Yes, in the GoFrame framework, a unified validation component is used wherever validation is involved. For details, please refer to the chapter: Data Validation

Let's compile it and see the effect:

$ main http
arguments validation failed for command "http": The Name field is required
1. arguments validation failed for command "http"
1). github.com/gogf/gf/v2/os/gcmd.newCommandFromMethod.func1
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/os/gcmd/gcmd_command_object.go:290
2). github.com/gogf/gf/v2/os/gcmd.(*Command).doRun
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/os/gcmd/gcmd_command_run.go:120
3). github.com/gogf/gf/v2/os/gcmd.(*Command).RunWithValueError
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/os/gcmd/gcmd_command_run.go:77
4). github.com/gogf/gf/v2/os/gcmd.(*Command).RunWithValue
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/os/gcmd/gcmd_command_run.go:32
5). github.com/gogf/gf/v2/os/gcmd.(*Command).Run
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.test/test.go:38
2. The Name field is required

Upon execution, there's an error due to data validation indicating that both mandatory parameters (Name/Port) must be passed.

tip

The error here prints stack information because the GoFrame framework uses a full error stack design, where all component errors come with a bottom-up error stack to facilitate quick error localization. Of course, we can obtain the returned error object and disable the stack information through the RunWithError method.

Let's add parameter input and try again:

$ main http my-http-server -p 8199
2022-01-19 22:52:45.808 [DEBU] openapi specification is disabled

SERVER | DOMAIN | ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE
-----------------|---------|---------|--------|-------|-----------------------------------------------------------------|--------------------
my-http-server | default | :8199 | ALL | / | main.(*cMain).Http.func1 |
-----------------|---------|---------|--------|-------|-----------------------------------------------------------------|--------------------
my-http-server | default | :8199 | ALL | /* | github.com/gogf/gf/v2/net/ghttp.internalMiddlewareServerTracing | GLOBAL MIDDLEWARE
-----------------|---------|---------|--------|-------|-----------------------------------------------------------------|--------------------

2022-01-19 22:52:45.810 66292: http server started listening on [:8199]

Yes, that's correct.

Complete Usage Example

The development tool of the GoFrame framework typically uses object-oriented, structured command line management. If interested, you can check the source code for more understanding: https://github.com/gogf/gf/tree/master/cmd/gf

Predefined Tags

In structured design, we use some struct tags, most of which originate from the attributes of the Command command. Let's introduce them here:

TagAbbreviationDescriptionNote
name-NameIf it is an input parameter structure, it will automatically read the method name as name when name is not specified
short-Command abbreviation
usage-Command usage
brief-Command description
arg-Indicates the input parameter is from a parameter rather than an optionOnly for attribute tags
orphan-Indicates the option is without parametersAttributes are usually of bool type
descriptiondcDetailed description of the command
additionaladAdditional description information of the command
examplesegUsage examples of the command
root-Specifies the subcommand name as the parent command, and other methods as its subcommandsOnly for main command object struct Meta tags
strict-Indicates the command strictly parses parameters/options, returning an error when unsupported parameters/options are inputOnly for object struct Meta tags
config-Indicates that option data for the command supports reading from a specified configuration, sourced from the default global singleton configuration objectOnly for method input struct Meta tags

Advanced Features

Automatic Data Conversion

Structured parameter input supports automatic data type conversion. You just need to define the data types, and the rest is handled by the framework components. Automatic data type conversion is present in many components of the framework, especially in parameter inputs for HTTP/GRPC services. The underlying data conversion component used is: Type Conversion

tip

The command line parameter data conversion uses case insensitive, and ignores special characters rules to match attribute fields. For example, if there is a Name field property in the input parameter structure, no matter whether the command line inputs name or NAME as a named parameter, it will be received by the Name field property.

Automatic Data Validation

Similarly, the data validation component is also a unified component. Please refer to the chapter: Data Validation for details.

Reading Data from Configuration

When the corresponding data is not passed in the command line, the input parameter's structure data supports automatic acquisition from the configuration component, which only needs to set the config tag in Meta. The configuration source is the default global singleton configuration object. You can refer to the example in the GoFrame framework development tool source code: https://github.com/gogf/gf/tree/master/cmd/gf