mirror of
https://github.com/ipfs/kubo.git
synced 2025-07-01 02:30:39 +08:00
@ -239,7 +239,7 @@ func optionText(cmd ...*cmds.Command) []string {
|
||||
lines = append(lines, "")
|
||||
}
|
||||
|
||||
names := sortByLength(opt.Names)
|
||||
names := sortByLength(opt.Names())
|
||||
if len(names) >= j+1 {
|
||||
lines[i] += fmt.Sprintf(optionFlag, names[j])
|
||||
}
|
||||
@ -262,13 +262,13 @@ func optionText(cmd ...*cmds.Command) []string {
|
||||
|
||||
// add option types to output
|
||||
for i, opt := range options {
|
||||
lines[i] += " " + fmt.Sprintf("%v", opt.Type)
|
||||
lines[i] += " " + fmt.Sprintf("%v", opt.Type())
|
||||
}
|
||||
lines = align(lines)
|
||||
|
||||
// add option descriptions to output
|
||||
for i, opt := range options {
|
||||
lines[i] += " - " + opt.Description
|
||||
lines[i] += " - " + opt.Description()
|
||||
}
|
||||
|
||||
return lines
|
||||
|
@ -46,10 +46,17 @@ func Parse(input []string, stdin *os.File, root *cmds.Command) (cmds.Request, *c
|
||||
return nil, cmd, path, err
|
||||
}
|
||||
|
||||
recursive, _, err := req.Option(cmds.RecShort).Bool()
|
||||
if err != nil {
|
||||
return nil, nil, nil, u.ErrCast()
|
||||
// if -r is provided, and it is associated with the package builtin
|
||||
// recursive path option, allow recursive file paths
|
||||
recursiveOpt := req.Option(cmds.RecShort)
|
||||
recursive := false
|
||||
if recursiveOpt != nil && recursiveOpt.Definition() == cmds.OptionRecursivePath {
|
||||
recursive, _, err = recursiveOpt.Bool()
|
||||
if err != nil {
|
||||
return nil, nil, nil, u.ErrCast()
|
||||
}
|
||||
}
|
||||
|
||||
stringArgs, fileArgs, err := parseArgs(stringVals, stdin, cmd.Arguments, recursive)
|
||||
if err != nil {
|
||||
return nil, cmd, path, err
|
||||
|
@ -11,7 +11,7 @@ func TestOptionParsing(t *testing.T) {
|
||||
subCmd := &commands.Command{}
|
||||
cmd := &commands.Command{
|
||||
Options: []commands.Option{
|
||||
commands.Option{Names: []string{"b"}, Type: commands.String},
|
||||
commands.StringOption("b", "some option"),
|
||||
},
|
||||
Subcommands: map[string]*commands.Command{
|
||||
"test": subCmd,
|
||||
|
@ -167,7 +167,7 @@ func (c *Command) Get(path []string) (*Command, error) {
|
||||
|
||||
// GetOptions gets the options in the given path of commands
|
||||
func (c *Command) GetOptions(path []string) (map[string]Option, error) {
|
||||
options := make([]Option, len(c.Options))
|
||||
options := make([]Option, 0, len(c.Options))
|
||||
|
||||
cmds, err := c.Resolve(path)
|
||||
if err != nil {
|
||||
@ -181,7 +181,7 @@ func (c *Command) GetOptions(path []string) (map[string]Option, error) {
|
||||
|
||||
optionsMap := make(map[string]Option)
|
||||
for _, opt := range options {
|
||||
for _, name := range opt.Names {
|
||||
for _, name := range opt.Names() {
|
||||
if _, found := optionsMap[name]; found {
|
||||
return nil, fmt.Errorf("Option name '%s' used multiple times", name)
|
||||
}
|
||||
|
@ -9,8 +9,8 @@ func noop(req Request) (interface{}, error) {
|
||||
func TestOptionValidation(t *testing.T) {
|
||||
cmd := Command{
|
||||
Options: []Option{
|
||||
Option{[]string{"b", "beep"}, Int, "enables beeper"},
|
||||
Option{[]string{"B", "boop"}, String, "password for booper"},
|
||||
IntOption("b", "beep", "enables beeper"),
|
||||
StringOption("B", "boop", "password for booper"),
|
||||
},
|
||||
Run: noop,
|
||||
}
|
||||
@ -93,14 +93,14 @@ func TestOptionValidation(t *testing.T) {
|
||||
func TestRegistration(t *testing.T) {
|
||||
cmdA := &Command{
|
||||
Options: []Option{
|
||||
Option{[]string{"beep"}, Int, "number of beeps"},
|
||||
IntOption("beep", "number of beeps"),
|
||||
},
|
||||
Run: noop,
|
||||
}
|
||||
|
||||
cmdB := &Command{
|
||||
Options: []Option{
|
||||
Option{[]string{"beep"}, Int, "number of beeps"},
|
||||
IntOption("beep", "number of beeps"),
|
||||
},
|
||||
Run: noop,
|
||||
Subcommands: map[string]*Command{
|
||||
@ -110,7 +110,7 @@ func TestRegistration(t *testing.T) {
|
||||
|
||||
cmdC := &Command{
|
||||
Options: []Option{
|
||||
Option{[]string{"encoding"}, String, "data encoding type"},
|
||||
StringOption("encoding", "data encoding type"),
|
||||
},
|
||||
Run: noop,
|
||||
}
|
||||
|
@ -17,14 +17,28 @@ const (
|
||||
)
|
||||
|
||||
// Option is used to specify a field that will be provided by a consumer
|
||||
type Option struct {
|
||||
Names []string // a list of unique names to
|
||||
Type reflect.Kind // value must be this type
|
||||
Description string // a short string to describe this option
|
||||
type Option interface {
|
||||
Names() []string // a list of unique names matched with user-provided flags
|
||||
Type() reflect.Kind // value must be this type
|
||||
Description() string // a short string that describes this option
|
||||
}
|
||||
|
||||
// MAYBE_TODO: add more features(?):
|
||||
//Default interface{} // the default value (ignored if `Required` is true)
|
||||
//Required bool // whether or not the option must be provided
|
||||
type option struct {
|
||||
names []string
|
||||
kind reflect.Kind
|
||||
description string
|
||||
}
|
||||
|
||||
func (o *option) Names() []string {
|
||||
return o.names
|
||||
}
|
||||
|
||||
func (o *option) Type() reflect.Kind {
|
||||
return o.kind
|
||||
}
|
||||
|
||||
func (o *option) Description() string {
|
||||
return o.description
|
||||
}
|
||||
|
||||
// constructor helper functions
|
||||
@ -37,10 +51,10 @@ func NewOption(kind reflect.Kind, names ...string) Option {
|
||||
desc := names[len(names)-1]
|
||||
names = names[:len(names)-1]
|
||||
|
||||
return Option{
|
||||
Names: names,
|
||||
Type: kind,
|
||||
Description: desc,
|
||||
return &option{
|
||||
names: names,
|
||||
kind: kind,
|
||||
description: desc,
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,6 +83,7 @@ func StringOption(names ...string) Option {
|
||||
type OptionValue struct {
|
||||
value interface{}
|
||||
found bool
|
||||
def Option
|
||||
}
|
||||
|
||||
// Found returns true if the option value was provided by the user (not a default value)
|
||||
@ -76,6 +91,11 @@ func (ov OptionValue) Found() bool {
|
||||
return ov.found
|
||||
}
|
||||
|
||||
// Definition returns the option definition for the provided value
|
||||
func (ov OptionValue) Definition() Option {
|
||||
return ov.def
|
||||
}
|
||||
|
||||
// value accessor methods, gets the value as a certain type
|
||||
func (ov OptionValue) Bool() (value bool, found bool, err error) {
|
||||
if !ov.found {
|
||||
@ -141,9 +161,12 @@ const (
|
||||
)
|
||||
|
||||
// options that are used by this package
|
||||
var OptionEncodingType = StringOption(EncShort, EncLong, "The encoding type the output should be encoded with (json, xml, or text)")
|
||||
var OptionRecursivePath = BoolOption(RecShort, RecLong, "Add directory paths recursively")
|
||||
|
||||
// global options, added to every command
|
||||
var globalOptions = []Option{
|
||||
StringOption(EncShort, EncLong, "The encoding type the output should be encoded with (json, xml, or text)"),
|
||||
BoolOption(RecShort, RecLong, "Add directory paths recursively"),
|
||||
OptionEncodingType,
|
||||
}
|
||||
|
||||
// the above array of Options, wrapped in a Command
|
||||
|
@ -92,13 +92,6 @@ func (r *request) Path() []string {
|
||||
|
||||
// Option returns the value of the option for given name.
|
||||
func (r *request) Option(name string) *OptionValue {
|
||||
val, found := r.options[name]
|
||||
if found {
|
||||
return &OptionValue{val, found}
|
||||
}
|
||||
|
||||
// if a value isn't defined for that name, we will try to look it up by its aliases
|
||||
|
||||
// find the option with the specified name
|
||||
option, found := r.optionDefs[name]
|
||||
if !found {
|
||||
@ -106,15 +99,15 @@ func (r *request) Option(name string) *OptionValue {
|
||||
}
|
||||
|
||||
// try all the possible names, break if we find a value
|
||||
for _, n := range option.Names {
|
||||
val, found = r.options[n]
|
||||
for _, n := range option.Names() {
|
||||
val, found := r.options[n]
|
||||
if found {
|
||||
return &OptionValue{val, found}
|
||||
return &OptionValue{val, found, option}
|
||||
}
|
||||
}
|
||||
|
||||
// MAYBE_TODO: use default value instead of nil
|
||||
return &OptionValue{nil, false}
|
||||
return &OptionValue{nil, false, option}
|
||||
}
|
||||
|
||||
// Options returns a copy of the option map
|
||||
@ -135,7 +128,7 @@ func (r *request) SetOption(name string, val interface{}) {
|
||||
}
|
||||
|
||||
// try all the possible names, if we already have a value then set over it
|
||||
for _, n := range option.Names {
|
||||
for _, n := range option.Names() {
|
||||
_, found := r.options[n]
|
||||
if found {
|
||||
r.options[n] = val
|
||||
@ -222,9 +215,9 @@ func (r *request) ConvertOptions() error {
|
||||
}
|
||||
|
||||
kind := reflect.TypeOf(v).Kind()
|
||||
if kind != opt.Type {
|
||||
if kind != opt.Type() {
|
||||
if kind == String {
|
||||
convert := converters[opt.Type]
|
||||
convert := converters[opt.Type()]
|
||||
str, ok := v.(string)
|
||||
if !ok {
|
||||
return u.ErrCast()
|
||||
@ -236,19 +229,19 @@ func (r *request) ConvertOptions() error {
|
||||
value = "empty value"
|
||||
}
|
||||
return fmt.Errorf("Could not convert %s to type '%s' (for option '-%s')",
|
||||
value, opt.Type.String(), k)
|
||||
value, opt.Type().String(), k)
|
||||
}
|
||||
r.options[k] = val
|
||||
|
||||
} else {
|
||||
return fmt.Errorf("Option '%s' should be type '%s', but got type '%s'",
|
||||
k, opt.Type.String(), kind.String())
|
||||
k, opt.Type().String(), kind.String())
|
||||
}
|
||||
} else {
|
||||
r.options[k] = v
|
||||
}
|
||||
|
||||
for _, name := range opt.Names {
|
||||
for _, name := range opt.Names() {
|
||||
if _, ok := r.options[name]; name != k && ok {
|
||||
return fmt.Errorf("Duplicate command options were provided ('%s' and '%s')",
|
||||
k, name)
|
||||
|
@ -39,6 +39,9 @@ remains to be implemented.
|
||||
Arguments: []cmds.Argument{
|
||||
cmds.FileArg("path", true, true, "The path to a file to be added to IPFS").EnableRecursive(),
|
||||
},
|
||||
Options: []cmds.Option{
|
||||
cmds.OptionRecursivePath, // a builtin option that allows recursive paths (-r, --recursive)
|
||||
},
|
||||
Run: func(req cmds.Request) (interface{}, error) {
|
||||
added := &AddOutput{}
|
||||
n, err := req.Context().GetNode()
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
cmds "github.com/jbenet/go-ipfs/commands"
|
||||
"github.com/jbenet/go-ipfs/core"
|
||||
"github.com/jbenet/go-ipfs/merkledag"
|
||||
u "github.com/jbenet/go-ipfs/util"
|
||||
)
|
||||
|
||||
var pinCmd = &cmds.Command{
|
||||
@ -16,6 +17,7 @@ var pinCmd = &cmds.Command{
|
||||
Subcommands: map[string]*cmds.Command{
|
||||
"add": addPinCmd,
|
||||
"rm": rmPinCmd,
|
||||
"ls": listPinCmd,
|
||||
},
|
||||
}
|
||||
|
||||
@ -99,6 +101,68 @@ collected if needed.
|
||||
},
|
||||
}
|
||||
|
||||
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.
|
||||
`,
|
||||
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")
|
||||
`,
|
||||
},
|
||||
|
||||
Options: []cmds.Option{
|
||||
cmds.StringOption("type", "t", "The type of pinned keys to list. Can be \"direct\", \"indirect\", \"recursive\", or \"all\". Defaults to \"direct\""),
|
||||
},
|
||||
Run: func(req cmds.Request) (interface{}, error) {
|
||||
n, err := req.Context().GetNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
typeStr, found, err := req.Option("type").String()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !found {
|
||||
typeStr = "direct"
|
||||
}
|
||||
|
||||
switch typeStr {
|
||||
case "all", "direct", "indirect", "recursive":
|
||||
default:
|
||||
return nil, cmds.ClientError("Invalid type '" + typeStr + "', must be one of {direct, indirect, recursive, all}")
|
||||
}
|
||||
|
||||
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()...)
|
||||
}
|
||||
|
||||
return &KeyList{Keys: keys}, nil
|
||||
},
|
||||
Type: &KeyList{},
|
||||
Marshalers: cmds.MarshalerMap{
|
||||
cmds.Text: KeyListTextMarshaler,
|
||||
},
|
||||
}
|
||||
|
||||
func pin(n *core.IpfsNode, paths []string, recursive bool) ([]*merkledag.Node, error) {
|
||||
|
||||
dagnodes := make([]*merkledag.Node, 0)
|
||||
|
@ -10,8 +10,19 @@ import (
|
||||
u "github.com/jbenet/go-ipfs/util"
|
||||
)
|
||||
|
||||
type RefsOutput struct {
|
||||
Refs []string
|
||||
// KeyList is a general type for outputting lists of keys
|
||||
type KeyList struct {
|
||||
Keys []u.Key
|
||||
}
|
||||
|
||||
// KeyListTextMarshaler outputs a KeyList as plaintext, one key per line
|
||||
func KeyListTextMarshaler(res cmds.Response) ([]byte, error) {
|
||||
output := res.Output().(*KeyList)
|
||||
s := ""
|
||||
for _, key := range output.Keys {
|
||||
s += key.B58String() + "\n"
|
||||
}
|
||||
return []byte(s), nil
|
||||
}
|
||||
|
||||
var refsCmd = &cmds.Command{
|
||||
@ -58,26 +69,19 @@ Note: list all refs recursively with -r.
|
||||
|
||||
return getRefs(n, req.Arguments(), unique, recursive)
|
||||
},
|
||||
Type: &RefsOutput{},
|
||||
Type: &KeyList{},
|
||||
Marshalers: cmds.MarshalerMap{
|
||||
cmds.Text: func(res cmds.Response) ([]byte, error) {
|
||||
output := res.Output().(*RefsOutput)
|
||||
s := ""
|
||||
for _, ref := range output.Refs {
|
||||
s += fmt.Sprintln(ref)
|
||||
}
|
||||
return []byte(s), nil
|
||||
},
|
||||
cmds.Text: KeyListTextMarshaler,
|
||||
},
|
||||
}
|
||||
|
||||
func getRefs(n *core.IpfsNode, paths []string, unique, recursive bool) (*RefsOutput, error) {
|
||||
func getRefs(n *core.IpfsNode, paths []string, unique, recursive bool) (*KeyList, error) {
|
||||
var refsSeen map[u.Key]bool
|
||||
if unique {
|
||||
refsSeen = make(map[u.Key]bool)
|
||||
}
|
||||
|
||||
refs := make([]string, 0)
|
||||
refs := make([]u.Key, 0)
|
||||
|
||||
for _, path := range paths {
|
||||
object, err := n.Resolver.ResolvePath(path)
|
||||
@ -91,10 +95,10 @@ func getRefs(n *core.IpfsNode, paths []string, unique, recursive bool) (*RefsOut
|
||||
}
|
||||
}
|
||||
|
||||
return &RefsOutput{refs}, nil
|
||||
return &KeyList{refs}, nil
|
||||
}
|
||||
|
||||
func addRefs(n *core.IpfsNode, object *dag.Node, refs []string, refsSeen map[u.Key]bool, recursive bool) ([]string, error) {
|
||||
func addRefs(n *core.IpfsNode, object *dag.Node, refs []u.Key, refsSeen map[u.Key]bool, recursive bool) ([]u.Key, error) {
|
||||
for _, link := range object.Links {
|
||||
var found bool
|
||||
found, refs = addRef(link.Hash, refs, refsSeen)
|
||||
@ -115,15 +119,16 @@ func addRefs(n *core.IpfsNode, object *dag.Node, refs []string, refsSeen map[u.K
|
||||
return refs, nil
|
||||
}
|
||||
|
||||
func addRef(h mh.Multihash, refs []string, refsSeen map[u.Key]bool) (bool, []string) {
|
||||
func addRef(h mh.Multihash, refs []u.Key, refsSeen map[u.Key]bool) (bool, []u.Key) {
|
||||
key := u.Key(h)
|
||||
if refsSeen != nil {
|
||||
_, found := refsSeen[u.Key(h)]
|
||||
_, found := refsSeen[key]
|
||||
if found {
|
||||
return true, refs
|
||||
}
|
||||
refsSeen[u.Key(h)] = true
|
||||
refsSeen[key] = true
|
||||
}
|
||||
|
||||
refs = append(refs, h.B58String())
|
||||
refs = append(refs, key)
|
||||
return false, refs
|
||||
}
|
||||
|
@ -65,3 +65,7 @@ func (i *indirectPin) Decrement(k util.Key) {
|
||||
func (i *indirectPin) HasKey(k util.Key) bool {
|
||||
return i.blockset.HasKey(k)
|
||||
}
|
||||
|
||||
func (i *indirectPin) Set() set.BlockSet {
|
||||
return i.blockset
|
||||
}
|
||||
|
18
pin/pin.go
18
pin/pin.go
@ -33,6 +33,9 @@ type Pinner interface {
|
||||
Unpin(util.Key, bool) error
|
||||
Flush() error
|
||||
GetManual() ManualPinner
|
||||
DirectKeys() []util.Key
|
||||
IndirectKeys() []util.Key
|
||||
RecursiveKeys() []util.Key
|
||||
}
|
||||
|
||||
// ManualPinner is for manually editing the pin structure
|
||||
@ -207,6 +210,21 @@ func LoadPinner(d ds.Datastore, dserv mdag.DAGService) (Pinner, error) {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// DirectKeys returns a slice containing the directly pinned keys
|
||||
func (p *pinner) DirectKeys() []util.Key {
|
||||
return p.directPin.GetKeys()
|
||||
}
|
||||
|
||||
// IndirectKeys returns a slice containing the indirectly pinned keys
|
||||
func (p *pinner) IndirectKeys() []util.Key {
|
||||
return p.indirPin.Set().GetKeys()
|
||||
}
|
||||
|
||||
// RecursiveKeys returns a slice containing the recursively pinned keys
|
||||
func (p *pinner) RecursiveKeys() []util.Key {
|
||||
return p.recursePin.GetKeys()
|
||||
}
|
||||
|
||||
// Flush encodes and writes pinner keysets to the datastore
|
||||
func (p *pinner) Flush() error {
|
||||
p.lock.RLock()
|
||||
|
Reference in New Issue
Block a user