commands.go 4.36 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 16
	oldcmds "github.com/ipfs/go-ipfs/commands"
	e "github.com/ipfs/go-ipfs/core/commands/e"

Steven Allen's avatar
Steven Allen committed
17 18
	cmds "gx/ipfs/QmP9vZfc5WSjfGTXmwX2EcicMFzmZ6fXn7HTdKYat6ccmH/go-ipfs-cmds"
	"gx/ipfs/QmQp2a2Hhb7F6eK2A5hN8f9aJy4mtkEikL9Zj4cgB7d1dD/go-ipfs-cmdkit"
19 20
)

Jan Winkelmann's avatar
Jan Winkelmann committed
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
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
}

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

	showOpts bool
51 52 53 54
}

type Option struct {
	Names []string
55 56
}

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

61 62 63 64
// 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
65
		Helptext: cmdkit.HelpText{
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
66 67 68
			Tagline:          "List all available commands.",
			ShortDescription: `Lists all available commands (and subcommands) and exits.`,
		},
Jan Winkelmann's avatar
Jan Winkelmann committed
69
		Options: []cmdkit.Option{
70
			cmdkit.BoolOption(flagsOptionName, "f", "Show command flags"),
71
		},
Jan Winkelmann's avatar
Jan Winkelmann committed
72
		Run: func(req cmds.Request, res cmds.ResponseEmitter) {
73
			rootCmd := cmd2outputCmd("ipfs", root)
Jan Winkelmann's avatar
Jan Winkelmann committed
74 75 76 77 78
			rootCmd.showOpts, _, _ = req.Option(flagsOptionName).Bool()
			err := res.Emit(&rootCmd)
			if err != nil {
				log.Error(err)
			}
79
		},
Jan Winkelmann's avatar
Jan Winkelmann committed
80 81 82
		Encoders: cmds.EncoderMap{
			cmds.Text: func(req cmds.Request) func(io.Writer) cmds.Encoder {
				return func(w io.Writer) cmds.Encoder { return &commandEncoder{w} }
83
			},
84
		},
85
		Type: Command{},
86
	}
87 88
}

89 90 91 92 93 94
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()}
	}

95 96
	output := Command{
		Name:        name,
Jan Winkelmann's avatar
Jan Winkelmann committed
97
		Subcommands: make([]Command, len(cmd.Subcommands)+len(cmd.OldSubcommands)),
98
		Options:     opts,
99 100
	}

Jan Winkelmann's avatar
Jan Winkelmann committed
101 102 103
	// we need to keep track of names because a name *might* be used by both a Subcommand and an OldSubscommand.
	names := make(map[string]struct{})

104 105
	i := 0
	for name, sub := range cmd.Subcommands {
Jan Winkelmann's avatar
Jan Winkelmann committed
106
		names[name] = struct{}{}
107
		output.Subcommands[i] = cmd2outputCmd(name, sub)
108 109 110
		i++
	}

Jan Winkelmann's avatar
Jan Winkelmann committed
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
	for name, sub := range cmd.OldSubcommands {
		if _, ok := names[name]; ok {
			continue
		}

		names[name] = struct{}{}
		output.Subcommands[i] = oldCmd2outputCmd(name, sub)
		i++
	}

	// trucate to the amount of names we actually have
	output.Subcommands = output.Subcommands[:len(names)]
	return output
}

func oldCmd2outputCmd(name string, cmd *oldcmds.Command) Command {
	opts := make([]Option, len(cmd.Options))
	for i, opt := range cmd.Options {
		opts[i] = Option{opt.Names()}
	}

	output := Command{
		Name:        name,
		Subcommands: make([]Command, len(cmd.Subcommands)),
		Options:     opts,
	}

	i := 0
	for name, sub := range cmd.Subcommands {
		output.Subcommands[i] = oldCmd2outputCmd(name, sub)
		i++
	}

144 145 146
	return output
}

147
func cmdPathStrings(cmd *Command, showOptions bool) []string {
148
	var cmds []string
149

150 151
	var recurse func(prefix string, cmd *Command)
	recurse = func(prefix string, cmd *Command) {
rht's avatar
rht committed
152 153
		newPrefix := prefix + cmd.Name
		cmds = append(cmds, newPrefix)
154 155
		if prefix != "" && showOptions {
			for _, options := range cmd.Options {
156
				var cmdOpts []string
157
				for _, flag := range options.Names {
158 159 160 161 162 163
					if len(flag) == 1 {
						flag = "-" + flag
					} else {
						flag = "--" + flag
					}
					cmdOpts = append(cmdOpts, newPrefix+" "+flag)
rht's avatar
rht committed
164
				}
165
				cmds = append(cmds, strings.Join(cmdOpts, " / "))
rht's avatar
rht committed
166 167
			}
		}
168
		for _, sub := range cmd.Subcommands {
rht's avatar
rht committed
169
			recurse(newPrefix+" ", &sub)
170
		}
171 172
	}

173 174 175
	recurse("", cmd)
	sort.Sort(sort.StringSlice(cmds))
	return cmds
176
}
Jan Winkelmann's avatar
Jan Winkelmann committed
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194

// 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
}