pin.go 4.95 KB
Newer Older
1 2 3
package commands

import (
Jeromy's avatar
Jeromy committed
4
	"bytes"
5
	"fmt"
Jeromy's avatar
Jeromy committed
6
	"io"
7 8

	cmds "github.com/jbenet/go-ipfs/commands"
9
	corerepo "github.com/jbenet/go-ipfs/core/repo"
10
	u "github.com/jbenet/go-ipfs/util"
11 12
)

13
var PinCmd = &cmds.Command{
14
	Helptext: cmds.HelpText{
Brian Tiger Chow's avatar
Brian Tiger Chow committed
15
		Tagline: "Pin (and unpin) objects to local storage",
16
	},
17

Brian Tiger Chow's avatar
Brian Tiger Chow committed
18 19 20
	Subcommands: map[string]*cmds.Command{
		"add": addPinCmd,
		"rm":  rmPinCmd,
21
		"ls":  listPinCmd,
Brian Tiger Chow's avatar
Brian Tiger Chow committed
22 23 24
	},
}

Jeromy's avatar
Jeromy committed
25 26 27 28
type PinOutput struct {
	Pinned []u.Key
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
29
var addPinCmd = &cmds.Command{
30 31 32 33
	Helptext: cmds.HelpText{
		Tagline: "Pins objects to local storage",
		ShortDescription: `
Retrieves the object named by <ipfs-path> and stores it locally
34
on disk.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
35
`,
36
	},
37

38
	Arguments: []cmds.Argument{
39
		cmds.StringArg("ipfs-path", true, true, "Path to object(s) to be pinned").EnableStdin(),
40 41
	},
	Options: []cmds.Option{
42
		cmds.BoolOption("recursive", "r", "Recursively pin the object linked to by the specified object(s)"),
43
	},
Jeromy's avatar
Jeromy committed
44
	Type: PinOutput{},
45
	Run: func(req cmds.Request, res cmds.Response) {
46 47
		n, err := req.Context().GetNode()
		if err != nil {
48 49
			res.SetError(err, cmds.ErrNormal)
			return
50
		}
51 52

		// set recursive flag
53 54
		recursive, found, err := req.Option("recursive").Bool()
		if err != nil {
55 56
			res.SetError(err, cmds.ErrNormal)
			return
57 58 59 60
		}
		if !found {
			recursive = false
		}
61

62
		added, err := corerepo.Pin(n, req.Arguments(), recursive)
63
		if err != nil {
64 65
			res.SetError(err, cmds.ErrNormal)
			return
66 67
		}

68
		res.SetOutput(&PinOutput{added})
Jeromy's avatar
Jeromy committed
69 70 71 72 73 74 75 76
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
			added, ok := res.Output().(*PinOutput)
			if !ok {
				return nil, u.ErrCast()
			}

Jeromy's avatar
Jeromy committed
77 78 79 80 81 82 83 84
			var pintype string
			rec, _, _ := res.Request().Option("recursive").Bool()
			if rec {
				pintype = "recursively"
			} else {
				pintype = "directly"
			}

Jeromy's avatar
Jeromy committed
85 86
			buf := new(bytes.Buffer)
			for _, k := range added.Pinned {
Jeromy's avatar
Jeromy committed
87
				fmt.Fprintf(buf, "pinned %s %s\n", k, pintype)
Jeromy's avatar
Jeromy committed
88 89 90
			}
			return buf, nil
		},
91 92 93
	},
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
94
var rmPinCmd = &cmds.Command{
95 96 97 98
	Helptext: cmds.HelpText{
		Tagline: "Unpin an object from local storage",
		ShortDescription: `
Removes the pin from the given object allowing it to be garbage
99
collected if needed.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
100
`,
101
	},
102

103
	Arguments: []cmds.Argument{
104
		cmds.StringArg("ipfs-path", true, true, "Path to object(s) to be unpinned").EnableStdin(),
105 106
	},
	Options: []cmds.Option{
107
		cmds.BoolOption("recursive", "r", "Recursively unpin the object linked to by the specified object(s)"),
108
	},
Jeromy's avatar
Jeromy committed
109
	Type: PinOutput{},
110
	Run: func(req cmds.Request, res cmds.Response) {
111 112
		n, err := req.Context().GetNode()
		if err != nil {
113 114
			res.SetError(err, cmds.ErrNormal)
			return
115
		}
116 117

		// set recursive flag
118 119
		recursive, found, err := req.Option("recursive").Bool()
		if err != nil {
120 121
			res.SetError(err, cmds.ErrNormal)
			return
122 123 124 125
		}
		if !found {
			recursive = false // default
		}
126

127
		removed, err := corerepo.Unpin(n, req.Arguments(), recursive)
128
		if err != nil {
129 130
			res.SetError(err, cmds.ErrNormal)
			return
131 132
		}

133
		res.SetOutput(&PinOutput{removed})
Jeromy's avatar
Jeromy committed
134 135 136 137 138 139 140 141 142 143
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
			added, ok := res.Output().(*PinOutput)
			if !ok {
				return nil, u.ErrCast()
			}

			buf := new(bytes.Buffer)
			for _, k := range added.Pinned {
Jeromy's avatar
Jeromy committed
144
				fmt.Fprintf(buf, "unpinned %s\n", k)
Jeromy's avatar
Jeromy committed
145 146 147
			}
			return buf, nil
		},
148 149
	},
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
150

151 152 153 154 155 156
var listPinCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "List objects pinned to local storage",
		ShortDescription: `
Returns a list of hashes of objects being pinned. Objects that are indirectly
or recursively pinned are not included in the list.
157 158 159 160 161 162 163 164 165 166 167
`,
		LongDescription: `
Returns a list of hashes of objects being pinned. Objects that are indirectly
or recursively pinned are not included in the list.

Use --type=<type> to specify the type of pinned keys to list. Valid values are:
    * "direct"
    * "indirect"
    * "recursive"
    * "all"
(Defaults to "direct")
168 169 170
`,
	},

171
	Options: []cmds.Option{
172
		cmds.StringOption("type", "t", "The type of pinned keys to list. Can be \"direct\", \"indirect\", \"recursive\", or \"all\". Defaults to \"direct\""),
173
	},
174
	Run: func(req cmds.Request, res cmds.Response) {
175 176
		n, err := req.Context().GetNode()
		if err != nil {
177 178
			res.SetError(err, cmds.ErrNormal)
			return
179 180
		}

181 182
		typeStr, found, err := req.Option("type").String()
		if err != nil {
183 184
			res.SetError(err, cmds.ErrNormal)
			return
185 186
		}
		if !found {
187
			typeStr = "direct"
188 189
		}

190 191 192
		switch typeStr {
		case "all", "direct", "indirect", "recursive":
		default:
193 194
			err = fmt.Errorf("Invalid type '%s', must be one of {direct, indirect, recursive, all}", typeStr)
			res.SetError(err, cmds.ErrClient)
195 196 197 198 199 200 201 202 203 204 205 206 207
		}

		keys := make([]u.Key, 0)
		if typeStr == "direct" || typeStr == "all" {
			keys = append(keys, n.Pinning.DirectKeys()...)
		}
		if typeStr == "indirect" || typeStr == "all" {
			keys = append(keys, n.Pinning.IndirectKeys()...)
		}
		if typeStr == "recursive" || typeStr == "all" {
			keys = append(keys, n.Pinning.RecursiveKeys()...)
		}

208
		res.SetOutput(&KeyList{Keys: keys})
209
	},
210
	Type: KeyList{},
211 212
	Marshalers: cmds.MarshalerMap{
		cmds.Text: KeyListTextMarshaler,
213 214
	},
}