go-ipfs-cmds 包源码分析
2018-07-25 本文已影响0人
站长_郭生
Command结构体分析
type Command struct {
Options []cmdkit.Option // 命令参数配置
Arguments []cmdkit.Argument // 传参
PreRun func(req *Request, env Environment) error //执行前处理参数等
// 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 // RUN执行逻辑
PostRun PostRunMap // map结构 后台处理业务逻辑
Encoders EncoderMap // map结构 encoder方式
Helptext cmdkit.HelpText // 帮助文档
// External denotes that a command is actually an external binary.
// fewer checks and validations will be performed on such commands.
External bool // 外部调用
// run返回 &Block{}, then Command.Type == &Block{}
Type interface{}
Subcommands map[string]*Command // 子命令
}
例子参考
package main
import (
"fmt"
"io"
"strconv"
"strings"
"time"
"gx/ipfs/QmdE4gMduCKCGAcczM2F5ioYDfdeKuPix138wrES1YSr7f/go-ipfs-cmdkit"
"gx/ipfs/QmNueRyPRQiV7PUEpnP4GgGLuK1rKQLaRW7sfPvUetYig1/go-ipfs-cmds"
"gx/ipfs/QmNueRyPRQiV7PUEpnP4GgGLuK1rKQLaRW7sfPvUetYig1/go-ipfs-cmds/cli"
"os"
"context"
"github.com/davecgh/go-spew/spew"
)
// AddStatus describes the progress of the add operation
type AddStatus struct {
// Current is the current value of the sum.
Current int
// Left is how many summands are left
Left int
}
// 定义根命令
var RootCmd = &cmds.Command{
// 定义子命令
Subcommands: map[string]*cmds.Command{
// the simplest way to make an adder
"simpleAdd": &cmds.Command{
// 接受参数
Arguments: []cmdkit.Argument{
cmdkit.StringArg("summands", true, true, "values that are supposed to be summed"),
},
// run方法
Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) {
sum := 0
for i, str := range req.Arguments {
num, err := strconv.Atoi(str)
if err != nil {
re.SetError(err, cmdkit.ErrNormal)
return
}
sum += num
re.Emit(fmt.Sprintf("intermediate result: %d; %d left", sum, len(req.Arguments)-i-1))
}
re.Emit(fmt.Sprintf("total: %d", sum))
},
},
// a bit more sophisticated
"encodeAdd": &cmds.Command{
Arguments: []cmdkit.Argument{
cmdkit.StringArg("summands", true, true, "values that are supposed to be summed"),
},
Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) {
sum := 0
for i, str := range req.Arguments {
num, err := strconv.Atoi(str)
if err != nil {
re.SetError(err, cmdkit.ErrNormal)
return
}
sum += num
re.Emit(&AddStatus{
Current: sum,
Left: len(req.Arguments) - i - 1,
})
time.Sleep(200 * time.Millisecond)
}
},
Type: &AddStatus{},
Encoders: cmds.EncoderMap{
// This defines how to encode these values as text. Other possible encodings are XML and JSON.
cmds.Text: cmds.MakeEncoder(func(req *cmds.Request, w io.Writer, v interface{}) error {
s, ok := v.(*AddStatus)
if !ok {
return fmt.Errorf("cast error, got type %T", v)
}
if s.Left == 0 {
fmt.Fprintln(w, "total:", s.Current)
} else {
fmt.Fprintf(w, "intermediate result: %d; %d left\n", s.Current, s.Left)
}
return nil
}),
},
},
// the best UX
"postRunAdd": &cmds.Command{
Arguments: []cmdkit.Argument{
cmdkit.StringArg("summands", true, true, "values that are supposed to be summed"),
},
// this is the same as for encoderAdd
Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) {
sum := 0
for i, str := range req.Arguments {
num, err := strconv.Atoi(str)
if err != nil {
re.SetError(err, cmdkit.ErrNormal)
return
}
sum += num
re.Emit(&AddStatus{
Current: sum,
Left: len(req.Arguments) - i - 1,
})
time.Sleep(200 * time.Millisecond)
}
},
Type: &AddStatus{},
PostRun: cmds.PostRunMap{
cmds.CLI: func(req *cmds.Request, re cmds.ResponseEmitter) cmds.ResponseEmitter {
reNext, res := cmds.NewChanResponsePair(req)
go func() {
defer re.Close()
defer fmt.Println()
// length of line at last iteration
var lastLen int
for {
v, err := res.Next()
if err == io.EOF {
return
}
if err == cmds.ErrRcvdError {
fmt.Println("\nreceived error:", res.Error())
return
}
if err != nil {
fmt.Println("\nerror:", err)
return
}
fmt.Print("\r" + strings.Repeat(" ", lastLen))
s := v.(*AddStatus)
if s.Left > 0 {
lastLen, _ = fmt.Printf("\rcalculation sum... current: %d; left: %d", s.Current, s.Left)
spew.Dump(v)
} else {
lastLen, _ = fmt.Printf("\rsum is %d.", s.Current)
}
}
}()
return reNext
},
},
},
// how to set program's return value
"exitAdd": &cmds.Command{
Arguments: []cmdkit.Argument{
cmdkit.StringArg("summands", true, true, "values that are supposed to be summed"),
},
// this is the same as for encoderAdd
Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) {
sum := 0
for i, str := range req.Arguments {
num, err := strconv.Atoi(str)
spew.Dump(" 参数>>>>>>>> ", num)
if err != nil {
re.SetError(err, cmdkit.ErrNormal)
return
}
sum += num
re.Emit(&AddStatus{
Current: sum,
Left: len(req.Arguments) - i - 1,
})
time.Sleep(200 * time.Millisecond)
}
},
Type: &AddStatus{},
PostRun: cmds.PostRunMap{
cmds.CLI: func(req *cmds.Request, re cmds.ResponseEmitter) cmds.ResponseEmitter {
reNext, res := cmds.NewChanResponsePair(req)
clire := re.(cli.ResponseEmitter)
go func() {
defer re.Close()
defer fmt.Println()
// length of line at last iteration
var lastLen int
var exit int
defer func() {
clire.Exit(exit)
}()
for {
v, err := res.Next()
if err == io.EOF {
return
}
if err == cmds.ErrRcvdError {
fmt.Println("\nreceived error:", res.Error())
break
}
if err != nil {
fmt.Println("\nerror:", err)
break
}
fmt.Print("\r" + strings.Repeat(" ", lastLen))
s := v.(*AddStatus)
if s.Left > 0 {
lastLen, _ = fmt.Printf("\rcalculation sum... current: %d; left: %d", s.Current, s.Left)
} else {
lastLen, _ = fmt.Printf("\rsum is %d.", s.Current)
exit = s.Current
}
}
}()
return reNext
},
},
},
},
}
func main() {
// parse the command path, arguments and options from the command line
req, err := cli.Parse(context.TODO(), os.Args[1:], os.Stdin, RootCmd)
if err != nil {
panic(err)
}
req.Options["encoding"] = cmds.Text
// create an emitter
re, retCh := cli.NewResponseEmitter(os.Stdout, os.Stderr, req.Command.Encoders["Text"], req)
if pr, ok := req.Command.PostRun[cmds.CLI]; ok {
re = pr(req, re)
}
// chan 管道阻塞 等待命令执行完成
wait := make(chan struct{})
// call command in background
go func() {
defer close(wait)
RootCmd.Call(req, re, nil)
}()
// wait until command has returned and exit
ret := <-retCh
<-wait
os.Exit(ret)
}
HTTP方式:
利用go-ipfs-cmds/http
h := http.NewHandler(nil, adder.RootCmd, http.NewServerConfig())
// create http rpc server
err := nethttp.ListenAndServe(":6798", h)
if err != nil {
panic(err)
}