Documentation ¶
Overview ¶
Package cmds helps building both standalone and client-server applications.
Semantics ¶
The basic building blocks are requests, commands, emitters and responses. A command consists of a description of the parameters and a function. The function is passed the request as well as an emitter as arguments. It does operations on the inputs and sends the results to the user by emitting them.
There are a number of emitters in this package and subpackages, but the user is free to create their own.
Commands ¶
A command is a struct containing the commands help text, a description of the arguments and options, the command's processing function and a type to let the caller know what type will be emitted. Optionally one of the functions PostRun and Encoder may be defined that consumes the function's emitted values and generates a visual representation for e.g. the terminal. Encoders work on a value-by-value basis, while PostRun operates on the value stream.
Emitters ¶
An emitter has the Emit method, that takes the command's function's output as an argument and passes it to the user.
type ResponseEmitter interface { Close() error CloseWithError(error) error SetLength(length uint64) Emit(value interface{}) error }
The command's function does not know what kind of emitter it works with, so the same function may run locally or on a server, using an rpc interface. Emitters can also send errors using the SetError method.
The user-facing emitter usually is the cli emitter. Values emitter here will be printed to the terminal using either the Encoders or the PostRun function.
Responses ¶
A response is a value that the user can read emitted values from.
type Response interface { Request() Request Error() *Error Length() uint64 Next() (interface{}, error) }
Responses have a method Next() that returns the next emitted value and an error value. If the last element has been received, the returned error value is io.EOF. If the application code has sent an error using SetError, the error ErrRcvdError is returned on next, indicating that the caller should call Error(). Depending on the reponse type, other errors may also occur.
Pipes ¶
Pipes are pairs (emitter, response), such that a value emitted on the emitter can be received in the response value. Most builtin emitters are "pipe" emitters. The most prominent examples are the channel pipe and the http pipe.
The channel pipe is backed by a channel. The only error value returned by the response is io.EOF, which happens when the channel is closed.
The http pipe is backed by an http connection. The response can also return other errors, e.g. if there are errors on the network.
Examples ¶
To get a better idea of what's going on, take a look at the examples at https://github.com/TRON-US/go-btfs-cmds/tree/master/examples.
Index ¶
- Constants
- Variables
- func ClientError(msg string) error
- func Copy(re ResponseEmitter, res Response) error
- func EmitChan(re ResponseEmitter, ch <-chan interface{}) error
- func EmitOnce(re ResponseEmitter, v interface{}) error
- func GetEncoder(req *Request, w io.Writer, def EncodingType) (encType EncodingType, enc Encoder, err error)
- func MakeEncoder(f func(*Request, io.Writer, interface{}) error) func(*Request) func(io.Writer) Encoder
- func MakeTypedEncoder(f interface{}) func(*Request) func(io.Writer) Encoder
- func NewChanResponsePair(req *Request) (ResponseEmitter, Response)
- type Argument
- type ArgumentType
- type Command
- func (c *Command) Call(req *Request, re ResponseEmitter, env Environment)
- func (c *Command) CheckArguments(req *Request) error
- func (c *Command) DebugValidate() map[string][]error
- func (c *Command) Get(path []string) (*Command, error)
- func (c *Command) GetOptions(path []string) (map[string]Option, error)
- func (c *Command) ProcessHelp()
- func (c *Command) Resolve(pth []string) ([]*Command, error)
- func (c *Command) Walk(visitor CommandVisitor)
- type CommandVisitor
- type Decoder
- type Encoder
- type EncoderFunc
- type EncoderMap
- type EncodingType
- type Environment
- type Error
- type ErrorType
- type Executor
- type Extra
- type Flusher
- type Function
- type HelpText
- type MakeEnvironment
- type MakeExecutor
- type MaybeError
- type OptMap
- type Option
- func BoolOption(names ...string) Option
- func FloatOption(names ...string) Option
- func Int64Option(names ...string) Option
- func IntOption(names ...string) Option
- func NewOption(kind reflect.Kind, names ...string) Option
- func StringOption(names ...string) Option
- func StringsOption(names ...string) Option
- func Uint64Option(names ...string) Option
- func UintOption(names ...string) Option
- type PostRunMap
- type PostRunType
- type ReqLog
- type ReqLogEntry
- type Request
- type Response
- type ResponseEmitter
- type Single
- type StdinArguments
- type TextEncoder
- type TimeEvaluate
Constants ¶
const ( // General purpose Undefined = "" // EncodingTypes JSON = "json" XML = "xml" Protobuf = "protobuf" Text = "text" TextNewline = "textnl" // PostRunTypes CLI = "cli" )
Supported EncodingType constants.
const ( Invalid = reflect.Invalid Bool = reflect.Bool Int = reflect.Int Uint = reflect.Uint Int64 = reflect.Int64 Uint64 = reflect.Uint64 Float = reflect.Float64 String = reflect.String Strings = reflect.Array )
Types of Command options
const ( EncShort = "enc" EncLong = "encoding" RecShort = "r" RecLong = "recursive" ChanOpt = "stream-channels" TimeoutOpt = "timeout" OptShortHelp = "h" OptLongHelp = "help" DerefLong = "dereference-args" StdinName = "stdin-name" Hidden = "hidden" HiddenShort = "H" Ignore = "ignore" IgnoreRules = "ignore-rules-path" )
Flag names
const DefaultOutputEncoding = JSON
DefaultOutputEncoding defines the default API output encoding.
Variables ¶
var ( // ErrNotCallable signals a command that cannot be called. ErrNotCallable = ClientError("this command cannot be called directly; try one of its subcommands.") // ErrNoFormatter signals that the command can not be formatted. ErrNoFormatter = ClientError("this command cannot be formatted to plain text") // ErrIncorrectType signales that the commands returned a value with unexpected type. ErrIncorrectType = errors.New("the command returned a value with a different type than expected") )
var ( ErrClosedEmitter = errors.New("cmds: emit on closed emitter") ErrClosingClosedEmitter = errors.New("cmds: closing closed emitter") )
var Decoders = map[EncodingType]func(w io.Reader) Decoder{ XML: func(r io.Reader) Decoder { return xml.NewDecoder(r) }, JSON: func(r io.Reader) Decoder { return json.NewDecoder(r) }, }
var Encoders = EncoderMap{ XML: func(req *Request) func(io.Writer) Encoder { return func(w io.Writer) Encoder { return xml.NewEncoder(w) } }, JSON: func(req *Request) func(io.Writer) Encoder { return func(w io.Writer) Encoder { return json.NewEncoder(w) } }, Text: func(req *Request) func(io.Writer) Encoder { return func(w io.Writer) Encoder { return TextEncoder{w: w} } }, TextNewline: func(req *Request) func(io.Writer) Encoder { return func(w io.Writer) Encoder { return TextEncoder{w: w, suffix: "\n"} } }, }
var OptionDerefArgs = BoolOption(DerefLong, "Symlinks supplied in arguments are dereferenced")
var OptionEncodingType = StringOption(EncLong, EncShort, "The encoding type the output should be encoded with (json, xml, or text)").WithDefault("text")
options that are used by this package
var OptionHidden = BoolOption(Hidden, HiddenShort, "Include files that are hidden. Only takes effect on recursive add.")
var OptionIgnore = StringsOption(Ignore, "A rule (.gitignore-stype) defining which file(s) should be ignored (variadic, experimental)")
var OptionIgnoreRules = StringOption(IgnoreRules, "A path to a file with .gitignore-style ignore rules (experimental)")
var OptionRecursivePath = BoolOption(RecLong, RecShort, "Add directory paths recursively")
var OptionStdinName = StringOption(StdinName, "Assign a name if the file source is stdin.")
var OptionStreamChannels = BoolOption(ChanOpt, "Stream channel output")
var OptionTimeout = StringOption(TimeoutOpt, "Set a global timeout on the command")
Functions ¶
func ClientError ¶
func Copy ¶
func Copy(re ResponseEmitter, res Response) error
Copy sends all values received on res to re. If res is closed, it closes re.
func EmitChan ¶
func EmitChan(re ResponseEmitter, ch <-chan interface{}) error
func EmitOnce ¶
func EmitOnce(re ResponseEmitter, v interface{}) error
EmitOnce is a helper that emits a value wrapped in Single, to signal that this will be the only value sent.
func GetEncoder ¶
func GetEncoder(req *Request, w io.Writer, def EncodingType) (encType EncodingType, enc Encoder, err error)
GetEncoder takes a request and returns returns the encoding type and the encoder.
func MakeEncoder ¶
func NewChanResponsePair ¶
func NewChanResponsePair(req *Request) (ResponseEmitter, Response)
Types ¶
type Argument ¶
type Argument struct { Name string Type ArgumentType Required bool // error if no value is specified Variadic bool // unlimited values can be specfied SupportsStdin bool // can accept stdin as a value Recursive bool // supports recursive file adding (with '-r' flag) Description string }
func (Argument) EnableRecursive ¶
func (Argument) EnableStdin ¶
type Command ¶
type Command struct { // Options defines the flags accepted by the command. Flags on specified // on parent commands are inherited by sub commands. Options []Option // Arguments defines the positional arguments for the command. These // arguments can be strings and/or files. // // The rules for valid arguments are as follows: // // 1. No required arguments may follow optional arguments. // 2. There can be at most one STDIN argument. // 3. There can be at most one variadic argument, and it must be last. Arguments []Argument // PreRun is run before Run. When executing a command on a remote // daemon, PreRun is always run in the local process. PreRun func(req *Request, env Environment) error // Run is the function that processes the request to generate a response. // Note that when executing the command over the HTTP API you can only read // after writing when using multipart requests. The request body will not be // available for reading after the HTTP connection has been written to. Run Function // PostRun is run after Run, and can transform results returned by run. // When executing a command on a remote daemon, PostRun is always run in // the local process. PostRun PostRunMap // Encoders encode results from Run (and/or PostRun) in the desired // encoding. Encoders EncoderMap // Helptext is the command's help text. Helptext HelpText // RunTimeout is the default timeout for this particular command. // The timeout limits the start of the Run function to the end of the // response emittance. // If unset (Duration = 0), there is no timeout. // If the global CLI --timeout is set, this timeout is overridden by // the --timeout value. RunTimeout time.Duration // External denotes that a command is actually an external binary. // fewer checks and validations will be performed on such commands. External bool // Type describes the type of the output of the Command's Run Function. // In precise terms, the value of Type is an instance of the return type of // the Run Function. // // ie. If command Run returns &Block{}, then Command.Type == &Block{} Type interface{} // Subcommands allow attaching sub commands to a command. // // Note: A command can specify both a Run function and Subcommands. If // invoked with no arguments, or an argument that matches no // sub commands, the Run function of the current command will be invoked. // // Take care when specifying both a Run function and Subcommands. A // simple typo in a sub command will invoke the parent command and may // end up returning a cryptic error to the user. Subcommands map[string]*Command // NoRemote denotes that a command cannot be executed in a remote environment NoRemote bool // NoLocal denotes that a command cannot be executed in a local environment NoLocal bool // Extra contains a set of other command-specific parameters Extra *Extra }
Command is a runnable command, with input arguments and options (flags). It can also have Subcommands, to group units of work into sets.
func (*Command) Call ¶
func (c *Command) Call(req *Request, re ResponseEmitter, env Environment)
Call invokes the command for the given Request
func (*Command) CheckArguments ¶
CheckArguments checks that we have all the required string arguments, loading any from stdin if necessary.
func (*Command) DebugValidate ¶
DebugValidate checks if the command tree is well-formed.
This operation is slow and should be called from tests only.
func (*Command) GetOptions ¶
GetOptions returns the options in the given path of commands
func (*Command) ProcessHelp ¶
func (c *Command) ProcessHelp()
func (*Command) Resolve ¶
Resolve returns the subcommands at the given path The returned set of subcommands starts with this command and therefore is always at least size 1
func (*Command) Walk ¶
func (c *Command) Walk(visitor CommandVisitor)
Walks tree of all subcommands (including this one)
type CommandVisitor ¶
type CommandVisitor func(*Command)
type Decoder ¶
type Decoder interface {
Decode(value interface{}) error
}
Decoder decodes values into value (which should be a pointer).
type Encoder ¶
type Encoder interface {
Encode(value interface{}) error
}
Encoder encodes values onto e.g. an io.Writer. Examples are json.Encoder and xml.Encoder.
type EncoderMap ¶
type EncoderMap map[EncodingType]EncoderFunc
type EncodingType ¶
type EncodingType string
EncodingType defines a supported encoding
func GetEncoding ¶
func GetEncoding(req *Request, def EncodingType) EncodingType
GetEncoding returns the EncodingType set in a request, falling back to JSON
type Error ¶
Error is a struct for marshalling errors
func (Error) MarshalJSON ¶
func (*Error) UnmarshalJSON ¶
type ErrorType ¶
type ErrorType uint
ErrorType signfies a category of errors
const ( // ErrNormal is a normal error. The command failed for some reason that's not a bug. ErrNormal ErrorType = iota // ErrClient means the client made an invalid request. ErrClient // ErrImplementation means there's a bug in the implementation. ErrImplementation // ErrRateLimited is returned when the operation has been rate-limited. ErrRateLimited // ErrForbidden is returned when the client doesn't have permission to // perform the requested operation. ErrForbidden // ErrorTimedOut is returned when the operation had timed out or was canceled. ErrTimedOut )
ErrorTypes convey what category of error ocurred
type Executor ¶
type Executor interface {
Execute(req *Request, re ResponseEmitter, env Environment) error
}
func NewExecutor ¶
type Extra ¶ added in v0.2.7
type Extra struct {
// contains filtered or unexported fields
}
Extra is a set of tag information for a command
type Function ¶
type Function func(*Request, ResponseEmitter, Environment) error
Function is the type of function that Commands use. It reads from the Request, and writes results to the ResponseEmitter.
type HelpText ¶
type HelpText struct { // required Tagline string // used in <cmd usage> ShortDescription string // used in DESCRIPTION SynopsisOptionsValues map[string]string // mappings for synopsis generator // optional - whole section overrides Usage string // overrides USAGE section LongDescription string // overrides DESCRIPTION section Options string // overrides OPTIONS section Arguments string // overrides ARGUMENTS section Subcommands string // overrides SUBCOMMANDS section Synopsis string // overrides SYNOPSIS field }
HelpText is a set of strings used to generate command help text. The help text follows formats similar to man pages, but not exactly the same.
type MakeEnvironment ¶
type MakeEnvironment func(context.Context, *Request) (Environment, error)
MakeEnvironment takes a context and the request to construct the environment that is passed to the command's Run function. The user can define a function like this to pass it to cli.Run.
type MakeExecutor ¶
MakeExecutor takes the request and environment variable to construct the executor that determines how to call the command - i.e. by calling Run or making an API request to a daemon. The user can define a function like this to pass it to cli.Run.
type MaybeError ¶
type MaybeError struct { Value interface{} // needs to be a pointer Error *Error // contains filtered or unexported fields }
func (*MaybeError) Get ¶
func (m *MaybeError) Get() (interface{}, error)
func (*MaybeError) UnmarshalJSON ¶
func (m *MaybeError) UnmarshalJSON(data []byte) error
type Option ¶
type Option interface { Name() string // the main name of the option Names() []string // a list of unique names matched with user-provided flags Type() reflect.Kind // value must be this type Description() string // a short string that describes this option WithDefault(interface{}) Option // sets the default value of the option Default() interface{} Parse(str string) (interface{}, error) }
Option is used to specify a field that will be provided by a consumer
func BoolOption ¶
func FloatOption ¶
func Int64Option ¶
func StringOption ¶
func StringsOption ¶ added in v0.2.0
func Uint64Option ¶
func UintOption ¶
type PostRunMap ¶
type PostRunMap map[PostRunType]func(Response, ResponseEmitter) error
PostRunMap is the map used in Command.PostRun.
type ReqLog ¶
type ReqLog struct { Requests []*ReqLogEntry // contains filtered or unexported fields }
ReqLog represents a request log.
func (*ReqLog) Add ¶
func (rl *ReqLog) Add(req *Request) *ReqLogEntry
Add ads an entry to the log for the given request.
func (*ReqLog) AddEntry ¶
func (rl *ReqLog) AddEntry(rle *ReqLogEntry)
AddEntry adds an entry to the log.
func (*ReqLog) ClearInactive ¶
func (rl *ReqLog) ClearInactive()
ClearInactive clears any inactive requests from the log.
func (*ReqLog) Finish ¶
func (rl *ReqLog) Finish(rle *ReqLogEntry)
func (*ReqLog) Report ¶
func (rl *ReqLog) Report() []*ReqLogEntry
Report generates a copy of all the entries in the requestlog
func (*ReqLog) SetKeepTime ¶
type ReqLogEntry ¶
type ReqLogEntry struct { StartTime time.Time EndTime time.Time Active bool Command string Options map[string]interface{} Args []string ID int }
ReqLogEntry represent a log entry for a request.
func (*ReqLogEntry) Copy ¶
func (r *ReqLogEntry) Copy() *ReqLogEntry
Copy copies a log entry and returns a pointer to the copy.
type Request ¶
type Request struct { Context context.Context Root, Command *Command Path []string Arguments []string Options OptMap Files files.Directory // contains filtered or unexported fields }
Request represents a call to a command from a consumer
func NewRequest ¶
func NewRequest(ctx context.Context, path []string, opts OptMap, args []string, file files.Directory, root *Command, ) (*Request, error)
NewRequest returns a request initialized with given arguments An non-nil error will be returned if the provided option values are invalid
func (*Request) BodyArgs ¶
func (req *Request) BodyArgs() StdinArguments
BodyArgs returns a scanner that returns arguments passed in the body as tokens.
Returns nil if there are no arguments to be consumed via stdin.
func (*Request) FillDefaults ¶
FillDefaults fills in default values if option has not been set.
func (*Request) ParseBodyArgs ¶
ParseBodyArgs parses arguments in the request body.
type Response ¶
type Response interface { Request() *Request Error() *Error Length() uint64 // Next returns the next emitted value. // The returned error can be a network or decoding error. Next() (interface{}, error) }
Response is the result of a command request. Response is returned to the client.
type ResponseEmitter ¶
type ResponseEmitter interface { // Close closes the underlying transport. Close() error // CloseWithError closes the underlying transport and makes subsequent read // calls return the passed error. CloseWithError(error) error // SetLength sets the length of the output // err is an interface{} so we don't have to manually convert to error. SetLength(length uint64) // Emit sends a value. // If value is io.Reader we just copy that to the connection // other values are marshalled. Emit(value interface{}) error // Record the key event in the emit, used for profile the program RecordEvent(str string) //Show the event report for time used while execute the command ShowEventReport() string }
ResponseEmitter encodes and sends the command code's output to the client. It is all a command can write to.
func NewFlushForwarder ¶
func NewFlushForwarder(re ResponseEmitter, f Flusher) ResponseEmitter
func NewWriterResponseEmitter ¶
func NewWriterResponseEmitter(w io.WriteCloser, req *Request) (ResponseEmitter, error)
NewWriterResponseEmitter creates a response emitter that sends responses to the given WriterCloser.
type Single ¶
type Single struct {
Value interface{}
}
Single can be used to signal to any ResponseEmitter that only one value will be emitted. This is important e.g. for the http.ResponseEmitter so it can set the HTTP headers appropriately.
type StdinArguments ¶
type StdinArguments interface { io.ReadCloser // Scan reads in the next argument and returns true if there is an // argument to read. Scan() bool // Argument returns the next argument. Argument() string // Err returns any errors encountered when reading in arguments. Err() error }
StdinArguments is used to iterate through arguments piped through stdin.
It closely mimics the bufio.Scanner interface but also implements the ReadCloser interface.
type TextEncoder ¶
type TextEncoder struct {
// contains filtered or unexported fields
}
func (TextEncoder) Encode ¶
func (e TextEncoder) Encode(v interface{}) error
type TimeEvaluate ¶ added in v0.2.4
func (*TimeEvaluate) RecordTime ¶ added in v0.2.4
func (t *TimeEvaluate) RecordTime(event string)
func (*TimeEvaluate) Report ¶ added in v0.2.4
func (t *TimeEvaluate) Report() string