refs.go 2.98 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
package commands

import (
	"fmt"

	mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
	cmds "github.com/jbenet/go-ipfs/commands"
	"github.com/jbenet/go-ipfs/core"
	dag "github.com/jbenet/go-ipfs/merkledag"
	u "github.com/jbenet/go-ipfs/util"
)

13 14
// KeyList is a general type for outputting lists of keys
type KeyList struct {
15
	Keys []u.Key
16 17 18 19 20
}

// KeyListTextMarshaler outputs a KeyList as plaintext, one key per line
func KeyListTextMarshaler(res cmds.Response) ([]byte, error) {
	output := res.Output().(*KeyList)
21 22 23 24
	s := ""
	for _, key := range output.Keys {
		s += key.B58String() + "\n"
	}
25
	return []byte(s), nil
26 27 28
}

var refsCmd = &cmds.Command{
29 30 31 32
	Helptext: cmds.HelpText{
		Tagline: "Lists link hashes from an object",
		ShortDescription: `
Retrieves the object named by <ipfs-path> and displays the link
33
hashes it contains, with the following format:
34

35 36
  <link base58 hash>

37 38 39
Note: list all refs recursively with -r.
`,
	},
40 41

	Arguments: []cmds.Argument{
42
		cmds.StringArg("ipfs-path", true, true, "Path to the object(s) to list refs from"),
43 44
	},
	Options: []cmds.Option{
45
		cmds.BoolOption("unique", "u", "Omit duplicate refs from output"),
46
		cmds.BoolOption("rec", "R", "Recursively list links of child nodes"),
47
	},
48
	Run: func(req cmds.Request) (interface{}, error) {
49 50 51 52
		n, err := req.Context().GetNode()
		if err != nil {
			return nil, err
		}
53

54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
		unique, found, err := req.Option("unique").Bool()
		if err != nil {
			return nil, err
		}
		if !found {
			unique = false
		}

		recursive, found, err := req.Option("recursive").Bool()
		if err != nil {
			return nil, err
		}
		if !found {
			recursive = false
		}
69

70
		return getRefs(n, req.Arguments(), unique, recursive)
71
	},
72
	Type: &KeyList{},
73
	Marshalers: cmds.MarshalerMap{
74
		cmds.Text: KeyListTextMarshaler,
75 76 77
	},
}

78
func getRefs(n *core.IpfsNode, paths []string, unique, recursive bool) (*KeyList, error) {
79 80 81 82 83
	var refsSeen map[u.Key]bool
	if unique {
		refsSeen = make(map[u.Key]bool)
	}

84
	refs := make([]u.Key, 0)
85 86 87 88 89 90 91 92 93 94 95 96 97

	for _, path := range paths {
		object, err := n.Resolver.ResolvePath(path)
		if err != nil {
			return nil, err
		}

		refs, err = addRefs(n, object, refs, refsSeen, recursive)
		if err != nil {
			return nil, err
		}
	}

98
	return &KeyList{refs}, nil
99 100
}

101
func addRefs(n *core.IpfsNode, object *dag.Node, refs []u.Key, refsSeen map[u.Key]bool, recursive bool) ([]u.Key, error) {
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
	for _, link := range object.Links {
		var found bool
		found, refs = addRef(link.Hash, refs, refsSeen)

		if recursive && !found {
			child, err := n.DAG.Get(u.Key(link.Hash))
			if err != nil {
				return nil, fmt.Errorf("cannot retrieve %s (%s)", link.Hash.B58String(), err)
			}

			refs, err = addRefs(n, child, refs, refsSeen, recursive)
			if err != nil {
				return nil, err
			}
		}
	}

	return refs, nil
}

122 123
func addRef(h mh.Multihash, refs []u.Key, refsSeen map[u.Key]bool) (bool, []u.Key) {
	key := u.Key(h)
124
	if refsSeen != nil {
125
		_, found := refsSeen[key]
126 127 128
		if found {
			return true, refs
		}
129
		refsSeen[key] = true
130 131
	}

132
	refs = append(refs, key)
133 134
	return false, refs
}