go-ipfs-cmds 包源码分析

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)
    }
    原文作者:站长_郭生
    原文地址: https://www.jianshu.com/p/e526a0bf7152
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞