commands.go 3.43 KB
Newer Older
1
// Package commands implements the ipfs command interface
2 3 4 5
//
// Using github.com/ipfs/go-ipfs/commands to define the command line and HTTP
// APIs.  This is the interface available to folks using IPFS from outside of
// the Go language.
6 7 8
package commands

import (
Jan Winkelmann's avatar
Jan Winkelmann committed
9
	"fmt"
10
	"io"
11
	"sort"
12
	"strings"
13

Jan Winkelmann's avatar
Jan Winkelmann committed
14 15
	e "github.com/ipfs/go-ipfs/core/commands/e"

16
	"gx/ipfs/QmSP88ryZkHSRn1fnngAaV2Vcn63WUJzAavnRM9CVdU1Ky/go-ipfs-cmdkit"
Steven Allen's avatar
Steven Allen committed
17
	cmds "gx/ipfs/QmXTmUCBtDUrzDYVzASogLiNph7EBuYqEgPL7QoHNMzUnz/go-ipfs-cmds"
18 19
)

Jan Winkelmann's avatar
Jan Winkelmann committed
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
type commandEncoder struct {
	w io.Writer
}

func (e *commandEncoder) Encode(v interface{}) error {
	var (
		cmd *Command
		ok  bool
	)

	if cmd, ok = v.(*Command); !ok {
		return fmt.Errorf(`core/commands: uenxpected type %T, expected *"core/commands".Command`, v)
	}

	for _, s := range cmdPathStrings(cmd, cmd.showOpts) {
		_, err := e.w.Write([]byte(s + "\n"))
		if err != nil {
			return err
		}
	}

	return nil
}

44 45 46
type Command struct {
	Name        string
	Subcommands []Command
47
	Options     []Option
Jan Winkelmann's avatar
Jan Winkelmann committed
48 49

	showOpts bool
50 51 52 53
}

type Option struct {
	Names []string
54 55
}

56
const (
57
	flagsOptionName = "flags"
58 59
)

60 61 62 63
// CommandsCmd takes in a root command,
// and returns a command that lists the subcommands in that root
func CommandsCmd(root *cmds.Command) *cmds.Command {
	return &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
64
		Helptext: cmdkit.HelpText{
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
65 66 67
			Tagline:          "List all available commands.",
			ShortDescription: `Lists all available commands (and subcommands) and exits.`,
		},
Jan Winkelmann's avatar
Jan Winkelmann committed
68
		Options: []cmdkit.Option{
69
			cmdkit.BoolOption(flagsOptionName, "f", "Show command flags"),
70
		},
keks's avatar
keks committed
71
		Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
72
			rootCmd := cmd2outputCmd("ipfs", root)
73
			rootCmd.showOpts, _ = req.Options[flagsOptionName].(bool)
keks's avatar
keks committed
74
			return cmds.EmitOnce(res, &rootCmd)
75
		},
Jan Winkelmann's avatar
Jan Winkelmann committed
76
		Encoders: cmds.EncoderMap{
77
			cmds.Text: func(req *cmds.Request) func(io.Writer) cmds.Encoder {
Jan Winkelmann's avatar
Jan Winkelmann committed
78
				return func(w io.Writer) cmds.Encoder { return &commandEncoder{w} }
79
			},
80
		},
81
		Type: Command{},
82
	}
83 84
}

85 86 87 88 89 90
func cmd2outputCmd(name string, cmd *cmds.Command) Command {
	opts := make([]Option, len(cmd.Options))
	for i, opt := range cmd.Options {
		opts[i] = Option{opt.Names()}
	}

91 92
	output := Command{
		Name:        name,
93
		Subcommands: make([]Command, 0, len(cmd.Subcommands)),
94
		Options:     opts,
95 96
	}

Jan Winkelmann's avatar
Jan Winkelmann committed
97
	for name, sub := range cmd.Subcommands {
98
		output.Subcommands = append(output.Subcommands, cmd2outputCmd(name, sub))
Jan Winkelmann's avatar
Jan Winkelmann committed
99 100
	}

101 102 103
	return output
}

104
func cmdPathStrings(cmd *Command, showOptions bool) []string {
105
	var cmds []string
106

107 108
	var recurse func(prefix string, cmd *Command)
	recurse = func(prefix string, cmd *Command) {
rht's avatar
rht committed
109 110
		newPrefix := prefix + cmd.Name
		cmds = append(cmds, newPrefix)
111 112
		if prefix != "" && showOptions {
			for _, options := range cmd.Options {
113
				var cmdOpts []string
114
				for _, flag := range options.Names {
115 116 117 118 119 120
					if len(flag) == 1 {
						flag = "-" + flag
					} else {
						flag = "--" + flag
					}
					cmdOpts = append(cmdOpts, newPrefix+" "+flag)
rht's avatar
rht committed
121
				}
122
				cmds = append(cmds, strings.Join(cmdOpts, " / "))
rht's avatar
rht committed
123 124
			}
		}
125
		for _, sub := range cmd.Subcommands {
rht's avatar
rht committed
126
			recurse(newPrefix+" ", &sub)
127
		}
128 129
	}

130 131 132
	recurse("", cmd)
	sort.Sort(sort.StringSlice(cmds))
	return cmds
133
}
Jan Winkelmann's avatar
Jan Winkelmann committed
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151

// changes here will also need to be applied at
// - ./dag/dag.go
// - ./object/object.go
// - ./files/files.go
// - ./unixfs/unixfs.go
func unwrapOutput(i interface{}) (interface{}, error) {
	var (
		ch <-chan interface{}
		ok bool
	)

	if ch, ok = i.(<-chan interface{}); !ok {
		return nil, e.TypeErr(ch, i)
	}

	return <-ch, nil
}