commands.go 3.46 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 17
	cmds "gx/ipfs/QmPTfgFTo9PFr1PvPKyKoeMgBvYPh6cX3aDP7DHKVbnCbi/go-ipfs-cmds"
	"gx/ipfs/QmSP88ryZkHSRn1fnngAaV2Vcn63WUJzAavnRM9CVdU1Ky/go-ipfs-cmdkit"
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
		},
Jeromy's avatar
Jeromy committed
71
		Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
72
			rootCmd := cmd2outputCmd("ipfs", root)
73
			rootCmd.showOpts, _ = req.Options[flagsOptionName].(bool)
74
			err := cmds.EmitOnce(res, &rootCmd)
Jan Winkelmann's avatar
Jan Winkelmann committed
75 76 77
			if err != nil {
				log.Error(err)
			}
78
		},
Jan Winkelmann's avatar
Jan Winkelmann committed
79
		Encoders: cmds.EncoderMap{
80
			cmds.Text: func(req *cmds.Request) func(io.Writer) cmds.Encoder {
Jan Winkelmann's avatar
Jan Winkelmann committed
81
				return func(w io.Writer) cmds.Encoder { return &commandEncoder{w} }
82
			},
83
		},
84
		Type: Command{},
85
	}
86 87
}

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

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

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

104 105 106
	return output
}

107
func cmdPathStrings(cmd *Command, showOptions bool) []string {
108
	var cmds []string
109

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

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

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