1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-09-11 15:15:58 +08:00
Files
kubo/commands/request.go
Tor Arne Vestbø bfd12114e7 Remove daemon InitDone guard in interrupt handler
Instead of just terminating right there and then, we cancel the
context, and let the daemon exit cleanly. This make take a few
seconds, as the node builder and its child processes do not
care too much about the context state while building nodes,
but this can be improved by injecting checks for ctx.Done()
before time-consuming steps.
2015-04-20 16:35:35 +02:00

300 lines
6.6 KiB
Go

package commands
import (
"errors"
"fmt"
"io"
"os"
"reflect"
"strconv"
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
"github.com/ipfs/go-ipfs/commands/files"
"github.com/ipfs/go-ipfs/core"
"github.com/ipfs/go-ipfs/repo/config"
u "github.com/ipfs/go-ipfs/util"
)
type OptMap map[string]interface{}
type Context struct {
// this Context is temporary. Will be replaced soon, as we get
// rid of this variable entirely.
Context context.Context
Online bool
ConfigRoot string
config *config.Config
LoadConfig func(path string) (*config.Config, error)
node *core.IpfsNode
ConstructNode func() (*core.IpfsNode, error)
}
// GetConfig returns the config of the current Command exection
// context. It may load it with the providied function.
func (c *Context) GetConfig() (*config.Config, error) {
var err error
if c.config == nil {
if c.LoadConfig == nil {
return nil, errors.New("nil LoadConfig function")
}
c.config, err = c.LoadConfig(c.ConfigRoot)
}
return c.config, err
}
// GetNode returns the node of the current Command exection
// context. It may construct it with the providied function.
func (c *Context) GetNode() (*core.IpfsNode, error) {
var err error
if c.node == nil {
if c.ConstructNode == nil {
return nil, errors.New("nil ConstructNode function")
}
c.node, err = c.ConstructNode()
}
return c.node, err
}
// NodeWithoutConstructing returns the underlying node variable
// so that clients may close it.
func (c *Context) NodeWithoutConstructing() *core.IpfsNode {
return c.node
}
// Request represents a call to a command from a consumer
type Request interface {
Path() []string
Option(name string) *OptionValue
Options() OptMap
SetOption(name string, val interface{})
SetOptions(opts OptMap) error
Arguments() []string
SetArguments([]string)
Files() files.File
SetFiles(files.File)
Context() *Context
SetContext(Context)
Command() *Command
Values() map[string]interface{}
Stdin() io.Reader
ConvertOptions() error
}
type request struct {
path []string
options OptMap
arguments []string
files files.File
cmd *Command
ctx Context
optionDefs map[string]Option
values map[string]interface{}
stdin io.Reader
}
// Path returns the command path of this request
func (r *request) Path() []string {
return r.path
}
// Option returns the value of the option for given name.
func (r *request) Option(name string) *OptionValue {
// find the option with the specified name
option, found := r.optionDefs[name]
if !found {
return nil
}
// try all the possible names, break if we find a value
for _, n := range option.Names() {
val, found := r.options[n]
if found {
return &OptionValue{val, found, option}
}
}
// MAYBE_TODO: use default value instead of nil
return &OptionValue{nil, false, option}
}
// Options returns a copy of the option map
func (r *request) Options() OptMap {
output := make(OptMap)
for k, v := range r.options {
output[k] = v
}
return output
}
// SetOption sets the value of the option for given name.
func (r *request) SetOption(name string, val interface{}) {
// find the option with the specified name
option, found := r.optionDefs[name]
if !found {
return
}
// try all the possible names, if we already have a value then set over it
for _, n := range option.Names() {
_, found := r.options[n]
if found {
r.options[n] = val
return
}
}
r.options[name] = val
}
// SetOptions sets the option values, unsetting any values that were previously set
func (r *request) SetOptions(opts OptMap) error {
r.options = opts
return r.ConvertOptions()
}
// Arguments returns the arguments slice
func (r *request) Arguments() []string {
return r.arguments
}
func (r *request) SetArguments(args []string) {
r.arguments = args
}
func (r *request) Files() files.File {
return r.files
}
func (r *request) SetFiles(f files.File) {
r.files = f
}
func (r *request) Context() *Context {
return &r.ctx
}
func (r *request) SetContext(ctx Context) {
r.ctx = ctx
}
func (r *request) Command() *Command {
return r.cmd
}
type converter func(string) (interface{}, error)
var converters = map[reflect.Kind]converter{
Bool: func(v string) (interface{}, error) {
if v == "" {
return true, nil
}
return strconv.ParseBool(v)
},
Int: func(v string) (interface{}, error) {
val, err := strconv.ParseInt(v, 0, 32)
if err != nil {
return nil, err
}
return int(val), err
},
Uint: func(v string) (interface{}, error) {
val, err := strconv.ParseUint(v, 0, 32)
if err != nil {
return nil, err
}
return int(val), err
},
Float: func(v string) (interface{}, error) {
return strconv.ParseFloat(v, 64)
},
}
func (r *request) Values() map[string]interface{} {
return r.values
}
func (r *request) Stdin() io.Reader {
return r.stdin
}
func (r *request) ConvertOptions() error {
for k, v := range r.options {
opt, ok := r.optionDefs[k]
if !ok {
continue
}
kind := reflect.TypeOf(v).Kind()
if kind != opt.Type() {
if kind == String {
convert := converters[opt.Type()]
str, ok := v.(string)
if !ok {
return u.ErrCast()
}
val, err := convert(str)
if err != nil {
value := fmt.Sprintf("value '%v'", v)
if len(str) == 0 {
value = "empty value"
}
return fmt.Errorf("Could not convert %s to type '%s' (for option '-%s')",
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())
}
} else {
r.options[k] = v
}
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)
}
}
}
return nil
}
// NewEmptyRequest initializes an empty request
func NewEmptyRequest() (Request, error) {
return NewRequest(nil, nil, nil, nil, nil, nil)
}
// NewRequest returns a request initialized with given arguments
// An non-nil error will be returned if the provided option values are invalid
func NewRequest(path []string, opts OptMap, args []string, file files.File, cmd *Command, optDefs map[string]Option) (Request, error) {
if path == nil {
path = make([]string, 0)
}
if opts == nil {
opts = make(OptMap)
}
if args == nil {
args = make([]string, 0)
}
if optDefs == nil {
optDefs = make(map[string]Option)
}
ctx := Context{Context: context.TODO()}
values := make(map[string]interface{})
req := &request{path, opts, args, file, cmd, ctx, optDefs, values, os.Stdin}
err := req.ConvertOptions()
if err != nil {
return nil, err
}
return req, nil
}