improve flag doc with go 1.10+ (#56)
This commit is contained in:
parent
e00ef3eb7c
commit
a337c1afcf
|
|
@ -0,0 +1,9 @@
|
||||||
|
// +build go1.10
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
func indent() string {
|
||||||
|
// In Go 1.10 and up, the flag package automatically
|
||||||
|
// adds the right indentation.
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
// +build !go1.10
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
func indent() string {
|
||||||
|
// In Go 1.9 and older, we need to add indentation
|
||||||
|
// after newlines in the flag doc strings.
|
||||||
|
return " \t"
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Command grpcurl makes GRPC requests (a la cURL, but HTTP/2). It can use a supplied descriptor
|
// Command grpcurl makes gRPC requests (a la cURL, but HTTP/2). It can use a supplied descriptor
|
||||||
// file, protobuf sources, or service reflection to translate JSON or text request data into the
|
// file, protobuf sources, or service reflection to translate JSON or text request data into the
|
||||||
// appropriate protobuf messages and vice versa for presenting the response contents.
|
// appropriate protobuf messages and vice versa for presenting the response contents.
|
||||||
package main
|
package main
|
||||||
|
|
@ -40,109 +40,110 @@ var (
|
||||||
|
|
||||||
isUnixSocket func() bool // nil when run on non-unix platform
|
isUnixSocket func() bool // nil when run on non-unix platform
|
||||||
|
|
||||||
help = flag.Bool("help", false,
|
help = flag.Bool("help", false, prettify(`
|
||||||
`Print usage instructions and exit.`)
|
Print usage instructions and exit.`))
|
||||||
printVersion = flag.Bool("version", false,
|
printVersion = flag.Bool("version", false, prettify(`
|
||||||
`Print version.`)
|
Print version.`))
|
||||||
plaintext = flag.Bool("plaintext", false,
|
plaintext = flag.Bool("plaintext", false, prettify(`
|
||||||
`Use plain-text HTTP/2 when connecting to server (no TLS).`)
|
Use plain-text HTTP/2 when connecting to server (no TLS).`))
|
||||||
insecure = flag.Bool("insecure", false,
|
insecure = flag.Bool("insecure", false, prettify(`
|
||||||
`Skip server certificate and domain verification. (NOT SECURE!). Not
|
Skip server certificate and domain verification. (NOT SECURE!) Not
|
||||||
valid with -plaintext option.`)
|
valid with -plaintext option.`))
|
||||||
cacert = flag.String("cacert", "",
|
cacert = flag.String("cacert", "", prettify(`
|
||||||
`File containing trusted root certificates for verifying the server.
|
File containing trusted root certificates for verifying the server.
|
||||||
Ignored if -insecure is specified.`)
|
Ignored if -insecure is specified.`))
|
||||||
cert = flag.String("cert", "",
|
cert = flag.String("cert", "", prettify(`
|
||||||
`File containing client certificate (public key), to present to the
|
File containing client certificate (public key), to present to the
|
||||||
server. Not valid with -plaintext option. Must also provide -key option.`)
|
server. Not valid with -plaintext option. Must also provide -key option.`))
|
||||||
key = flag.String("key", "",
|
key = flag.String("key", "", prettify(`
|
||||||
`File containing client private key, to present to the server. Not valid
|
File containing client private key, to present to the server. Not valid
|
||||||
with -plaintext option. Must also provide -cert option.`)
|
with -plaintext option. Must also provide -cert option.`))
|
||||||
protoset multiString
|
protoset multiString
|
||||||
protoFiles multiString
|
protoFiles multiString
|
||||||
importPaths multiString
|
importPaths multiString
|
||||||
addlHeaders multiString
|
addlHeaders multiString
|
||||||
rpcHeaders multiString
|
rpcHeaders multiString
|
||||||
reflHeaders multiString
|
reflHeaders multiString
|
||||||
authority = flag.String("authority", "",
|
authority = flag.String("authority", "", prettify(`
|
||||||
`:authority pseudo header value to be passed along with underlying HTTP/2
|
Value of :authority pseudo-header to be use with underlying HTTP/2
|
||||||
requests. It defaults to 'host [ ":" port ]' part of the target url.`)
|
requests. It defaults to the given address.`))
|
||||||
data = flag.String("d", "",
|
data = flag.String("d", "", prettify(`
|
||||||
`Data for request contents. If the value is '@' then the request contents
|
Data for request contents. If the value is '@' then the request contents
|
||||||
are read from stdin. For calls that accept a stream of requests, the
|
are read from stdin. For calls that accept a stream of requests, the
|
||||||
contents should include all such request messages concatenated together
|
contents should include all such request messages concatenated together
|
||||||
(optionally separated by whitespace).`)
|
(possibly delimited; see -format).`))
|
||||||
format = flag.String("format", "json",
|
format = flag.String("format", "json", prettify(`
|
||||||
`The format of request data. The allowed values are 'json' or 'text'. For
|
The format of request data. The allowed values are 'json' or 'text'. For
|
||||||
'json', the input data must be in JSON format. Multiple request values may
|
'json', the input data must be in JSON format. Multiple request values
|
||||||
be concatenated (messages with a JSON representation other than object
|
may be concatenated (messages with a JSON representation other than
|
||||||
must be separated by whitespace, such as a newline). For 'text', the input
|
object must be separated by whitespace, such as a newline). For 'text',
|
||||||
data must be in the protobuf text format, in which case multiple request
|
the input data must be in the protobuf text format, in which case
|
||||||
values must be separated by the "record separate" ASCII character: 0x1E.
|
multiple request values must be separated by the "record separator"
|
||||||
The stream should not end in a record separator. If it does, it will be
|
ASCII character: 0x1E. The stream should not end in a record separator.
|
||||||
interpreted as a final, blank message after the separator.`)
|
If it does, it will be interpreted as a final, blank message after the
|
||||||
connectTimeout = flag.String("connect-timeout", "",
|
separator.`))
|
||||||
`The maximum time, in seconds, to wait for connection to be established.
|
connectTimeout = flag.String("connect-timeout", "", prettify(`
|
||||||
Defaults to 10 seconds.`)
|
The maximum time, in seconds, to wait for connection to be established.
|
||||||
keepaliveTime = flag.String("keepalive-time", "",
|
Defaults to 10 seconds.`))
|
||||||
`If present, the maximum idle time in seconds, after which a keepalive
|
keepaliveTime = flag.String("keepalive-time", "", prettify(`
|
||||||
probe is sent. If the connection remains idle and no keepalive response
|
If present, the maximum idle time in seconds, after which a keepalive
|
||||||
is received for this same period then the connection is closed and the
|
probe is sent. If the connection remains idle and no keepalive response
|
||||||
operation fails.`)
|
is received for this same period then the connection is closed and the
|
||||||
maxTime = flag.String("max-time", "",
|
operation fails.`))
|
||||||
`The maximum total time the operation can take. This is useful for
|
maxTime = flag.String("max-time", "", prettify(`
|
||||||
preventing batch jobs that use grpcurl from hanging due to slow or bad
|
The maximum total time the operation can take. This is useful for
|
||||||
network links or due to incorrect stream method usage.`)
|
preventing batch jobs that use grpcurl from hanging due to slow or bad
|
||||||
emitDefaults = flag.Bool("emit-defaults", false,
|
network links or due to incorrect stream method usage.`))
|
||||||
`Emit default values for JSON-encoded responses.`)
|
emitDefaults = flag.Bool("emit-defaults", false, prettify(`
|
||||||
msgTemplate = flag.Bool("msg-template", false,
|
Emit default values for JSON-encoded responses.`))
|
||||||
`When describing messages, show a template of input data.`)
|
msgTemplate = flag.Bool("msg-template", false, prettify(`
|
||||||
verbose = flag.Bool("v", false,
|
When describing messages, show a template of input data.`))
|
||||||
`Enable verbose output.`)
|
verbose = flag.Bool("v", false, prettify(`
|
||||||
serverName = flag.String("servername", "", "Override servername when validating TLS certificate.")
|
Enable verbose output.`))
|
||||||
|
serverName = flag.String("servername", "", prettify(`
|
||||||
|
Override server name when validating TLS certificate.`))
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
flag.Var(&addlHeaders, "H",
|
flag.Var(&addlHeaders, "H", prettify(`
|
||||||
`Additional headers in 'name: value' format. May specify more than one
|
Additional headers in 'name: value' format. May specify more than one
|
||||||
via multiple flags. These headers will also be included in reflection
|
via multiple flags. These headers will also be included in reflection
|
||||||
requests requests to a server.`)
|
requests requests to a server.`))
|
||||||
flag.Var(&rpcHeaders, "rpc-header",
|
flag.Var(&rpcHeaders, "rpc-header", prettify(`
|
||||||
`Additional RPC headers in 'name: value' format. May specify more than
|
Additional RPC headers in 'name: value' format. May specify more than
|
||||||
one via multiple flags. These headers will *only* be used when invoking
|
one via multiple flags. These headers will *only* be used when invoking
|
||||||
the requested RPC method. They are excluded from reflection requests.`)
|
the requested RPC method. They are excluded from reflection requests.`))
|
||||||
flag.Var(&reflHeaders, "reflect-header",
|
flag.Var(&reflHeaders, "reflect-header", prettify(`
|
||||||
`Additional reflection headers in 'name: value' format. May specify more
|
Additional reflection headers in 'name: value' format. May specify more
|
||||||
than one via multiple flags. These headers will only be used during
|
than one via multiple flags. These headers will *only* be used during
|
||||||
reflection requests and will be excluded when invoking the requested RPC
|
reflection requests and will be excluded when invoking the requested RPC
|
||||||
method.`)
|
method.`))
|
||||||
flag.Var(&protoset, "protoset",
|
flag.Var(&protoset, "protoset", prettify(`
|
||||||
`The name of a file containing an encoded FileDescriptorSet. This file's
|
The name of a file containing an encoded FileDescriptorSet. This file's
|
||||||
contents will be used to determine the RPC schema instead of querying
|
contents will be used to determine the RPC schema instead of querying
|
||||||
for it from the remote server via the GRPC reflection API. When set: the
|
for it from the remote server via the gRPC reflection API. When set: the
|
||||||
'list' action lists the services found in the given descriptors (vs.
|
'list' action lists the services found in the given descriptors (vs.
|
||||||
those exposed by the remote server), and the 'describe' action describes
|
those exposed by the remote server), and the 'describe' action describes
|
||||||
symbols found in the given descriptors. May specify more than one via
|
symbols found in the given descriptors. May specify more than one via
|
||||||
multiple -protoset flags. It is an error to use both -protoset and
|
multiple -protoset flags. It is an error to use both -protoset and
|
||||||
-proto flags.`)
|
-proto flags.`))
|
||||||
flag.Var(&protoFiles, "proto",
|
flag.Var(&protoFiles, "proto", prettify(`
|
||||||
`The name of a proto source file. Source files given will be used to
|
The name of a proto source file. Source files given will be used to
|
||||||
determine the RPC schema instead of querying for it from the remote
|
determine the RPC schema instead of querying for it from the remote
|
||||||
server via the GRPC reflection API. When set: the 'list' action lists
|
server via the gRPC reflection API. When set: the 'list' action lists
|
||||||
the services found in the given files and their imports (vs. those
|
the services found in the given files and their imports (vs. those
|
||||||
exposed by the remote server), and the 'describe' action describes
|
exposed by the remote server), and the 'describe' action describes
|
||||||
symbols found in the given files. May specify more than one via
|
symbols found in the given files. May specify more than one via multiple
|
||||||
multiple -proto flags. Imports will be resolved using the given
|
-proto flags. Imports will be resolved using the given -import-path
|
||||||
-import-path flags. Multiple proto files can be specified by specifying
|
flags. Multiple proto files can be specified by specifying multiple
|
||||||
multiple -proto flags. It is an error to use both -protoset and -proto
|
-proto flags. It is an error to use both -protoset and -proto flags.`))
|
||||||
flags.`)
|
flag.Var(&importPaths, "import-path", prettify(`
|
||||||
flag.Var(&importPaths, "import-path",
|
The path to a directory from which proto sources can be imported, for
|
||||||
`The path to a directory from which proto sources can be imported,
|
use with -proto flags. Multiple import paths can be configured by
|
||||||
for use with -proto flags. Multiple import paths can be configured by
|
specifying multiple -import-path flags. Paths will be searched in the
|
||||||
specifying multiple -import-path flags. Paths will be searched in the
|
order given. If no import paths are given, all files (including all
|
||||||
order given. If no import paths are given, all files (including all
|
imports) must be provided as -proto flags, and grpcurl will attempt to
|
||||||
imports) must be provided as -proto flags, and grpcurl will attempt to
|
resolve all import statements from the set of file names given.`))
|
||||||
resolve all import statements from the set of file names given.`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type multiString []string
|
type multiString []string
|
||||||
|
|
@ -184,6 +185,9 @@ func main() {
|
||||||
if *format != "json" && *format != "text" {
|
if *format != "json" && *format != "text" {
|
||||||
fail(nil, "The -format option must be 'json' or 'text.")
|
fail(nil, "The -format option must be 'json' or 'text.")
|
||||||
}
|
}
|
||||||
|
if *emitDefaults && *format != "json" {
|
||||||
|
warn("The -emit-defaults is only used when using json format.")
|
||||||
|
}
|
||||||
|
|
||||||
args := flag.Args()
|
args := flag.Args()
|
||||||
|
|
||||||
|
|
@ -496,8 +500,8 @@ func usage() {
|
||||||
fmt.Fprintf(os.Stderr, `Usage:
|
fmt.Fprintf(os.Stderr, `Usage:
|
||||||
%s [flags] [address] [list|describe] [symbol]
|
%s [flags] [address] [list|describe] [symbol]
|
||||||
|
|
||||||
The 'host:port' is only optional when used with 'list' or 'describe' and a
|
The 'address' is only optional when used with 'list' or 'describe' and a
|
||||||
protoset flag is provided.
|
protoset or proto flag is provided.
|
||||||
|
|
||||||
If 'list' is indicated, the symbol (if present) should be a fully-qualified
|
If 'list' is indicated, the symbol (if present) should be a fully-qualified
|
||||||
service name. If present, all methods of that service are listed. If not
|
service name. If present, all methods of that service are listed. If not
|
||||||
|
|
@ -509,17 +513,36 @@ is given then the descriptors for all exposed or known services are shown.
|
||||||
|
|
||||||
If neither verb is present, the symbol must be a fully-qualified method name in
|
If neither verb is present, the symbol must be a fully-qualified method name in
|
||||||
'service/method' or 'service.method' format. In this case, the request body will
|
'service/method' or 'service.method' format. In this case, the request body will
|
||||||
be used to invoke the named method. If no body is given, an empty instance of
|
be used to invoke the named method. If no body is given but one is required
|
||||||
the method's request type will be sent.
|
(i.e. the method is unary or server-streaming), an empty instance of the
|
||||||
|
method's request type will be sent.
|
||||||
|
|
||||||
The address will typically be in the form "host:port" where host can be an IP
|
The address will typically be in the form "host:port" where host can be an IP
|
||||||
address or a hostname and port is a numeric port or service name. If an IPv6
|
address or a hostname and port is a numeric port or service name. If an IPv6
|
||||||
address is given, it must be surrounded by brackets, like "[2001:db8::1]". For
|
address is given, it must be surrounded by brackets, like "[2001:db8::1]". For
|
||||||
Unix variants, if a -unix=true flag is present, then the address must be the
|
Unix variants, if a -unix=true flag is present, then the address must be the
|
||||||
path to the domain socket.
|
path to the domain socket.
|
||||||
|
|
||||||
|
Available flags:
|
||||||
`, os.Args[0])
|
`, os.Args[0])
|
||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
|
}
|
||||||
|
|
||||||
|
func prettify(docString string) string {
|
||||||
|
parts := strings.Split(docString, "\n")
|
||||||
|
|
||||||
|
// cull empty lines and also remove trailing and leading spaces
|
||||||
|
// from each line in the doc string
|
||||||
|
j := 0
|
||||||
|
for _, part := range parts {
|
||||||
|
if part == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts[j] = strings.TrimSpace(part)
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(parts[:j], "\n"+indent())
|
||||||
}
|
}
|
||||||
|
|
||||||
func warn(msg string, args ...interface{}) {
|
func warn(msg string, args ...interface{}) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"flag"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFlagDocIndent(t *testing.T) {
|
||||||
|
// Tests the prettify() and indent() function. The indent() function
|
||||||
|
// differs by Go version, due to differences in "flags" package across
|
||||||
|
// versions. Run with multiple versions of Go to ensure that doc output
|
||||||
|
// is properly indented, regardless of Go version.
|
||||||
|
|
||||||
|
var fs flag.FlagSet
|
||||||
|
var buf bytes.Buffer
|
||||||
|
fs.SetOutput(&buf)
|
||||||
|
|
||||||
|
fs.String("foo", "", prettify(`
|
||||||
|
This is a flag doc string.
|
||||||
|
It has multiple lines.
|
||||||
|
More than two, actually.`))
|
||||||
|
fs.Int("bar", 100, prettify(`This is a simple flag doc string.`))
|
||||||
|
fs.Bool("baz", false, prettify(`
|
||||||
|
This is another long doc string.
|
||||||
|
It also has multiple lines. But not as long as the first one.`))
|
||||||
|
|
||||||
|
fs.PrintDefaults()
|
||||||
|
|
||||||
|
expected :=
|
||||||
|
` -bar int
|
||||||
|
This is a simple flag doc string. (default 100)
|
||||||
|
-baz
|
||||||
|
This is another long doc string.
|
||||||
|
It also has multiple lines. But not as long as the first one.
|
||||||
|
-foo string
|
||||||
|
This is a flag doc string.
|
||||||
|
It has multiple lines.
|
||||||
|
More than two, actually.
|
||||||
|
`
|
||||||
|
|
||||||
|
actual := buf.String()
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("Flag output had wrong indentation.\nExpecting:\n%s\nGot:\n%s", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,8 +5,8 @@ package main
|
||||||
import "flag"
|
import "flag"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
unix = flag.Bool("unix", false,
|
unix = flag.Bool("unix", false, prettify(`
|
||||||
`Indicates that the server address is the path to a Unix domain socket.`)
|
Indicates that the server address is the path to a Unix domain socket.`))
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue