github.com/vektra/tachyon@v0.0.0-20150921164542-0da4f3861aef/command.go (about)

     1  package tachyon
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"reflect"
     7  	"strings"
     8  	"sync"
     9  )
    10  
    11  type ResultData map[string]Value
    12  
    13  func (rd ResultData) Set(key string, v interface{}) {
    14  	rd[key] = Any(v)
    15  }
    16  
    17  func (rd ResultData) Get(key string) interface{} {
    18  	if a, ok := rd[key]; !ok {
    19  		return nil
    20  	} else {
    21  		return a.Read()
    22  	}
    23  }
    24  
    25  type Result struct {
    26  	Changed bool
    27  	Failed  bool
    28  	Data    ResultData
    29  }
    30  
    31  func (r *Result) MarshalJSON() ([]byte, error) {
    32  	o := make(map[string]interface{})
    33  	m := make(map[string]interface{})
    34  
    35  	o["changed"] = r.Changed
    36  	o["failed"] = r.Failed
    37  	o["data"] = m
    38  
    39  	for k, v := range r.Data {
    40  		m[k] = v.Read()
    41  	}
    42  
    43  	return json.Marshal(o)
    44  }
    45  
    46  func (r *Result) Get(key string) (Value, bool) {
    47  	v, ok := r.Data[key]
    48  
    49  	return v, ok
    50  }
    51  
    52  func (r *Result) Add(key string, v interface{}) {
    53  	r.Data[key] = Any(v)
    54  }
    55  
    56  func WrapResult(changed bool, data ResultData) *Result {
    57  	return &Result{changed, false, data}
    58  }
    59  
    60  func NewResult(changed bool) *Result {
    61  	return &Result{changed, false, make(ResultData)}
    62  }
    63  
    64  func FailureResult(err error) *Result {
    65  	res := &Result{false, true, make(ResultData)}
    66  	res.Add("error", err.Error())
    67  
    68  	return res
    69  }
    70  
    71  type CommandEnv struct {
    72  	Env      *Environment
    73  	Paths    Paths
    74  	progress ProgressReporter
    75  }
    76  
    77  func NewCommandEnv(env *Environment, task *Task) *CommandEnv {
    78  	return &CommandEnv{
    79  		Env:      env,
    80  		Paths:    task.Paths,
    81  		progress: env.report,
    82  	}
    83  }
    84  
    85  func (e *CommandEnv) Progress(str string) {
    86  	if e.progress == nil {
    87  		fmt.Printf("=== %s\n", str)
    88  	} else {
    89  		e.progress.Progress(str)
    90  	}
    91  }
    92  
    93  type Command interface {
    94  	Run(env *CommandEnv) (*Result, error)
    95  }
    96  
    97  type ArgParser interface {
    98  	ParseArgs(s Scope, args string) (Vars, error)
    99  }
   100  
   101  type Commands map[string]reflect.Type
   102  
   103  var AvailableCommands Commands
   104  
   105  var initAvailable sync.Once
   106  
   107  func RegisterCommand(name string, cmd Command) {
   108  	initAvailable.Do(func() {
   109  		AvailableCommands = make(Commands)
   110  	})
   111  
   112  	ref := reflect.ValueOf(cmd)
   113  	e := ref.Elem()
   114  
   115  	AvailableCommands[name] = e.Type()
   116  }
   117  
   118  func MakeCommand(s Scope, task *Task, args string) (Command, Vars, error) {
   119  	name := task.Command()
   120  
   121  	t, ok := AvailableCommands[name]
   122  
   123  	if !ok {
   124  		return nil, nil, fmt.Errorf("Unknown command: %s", name)
   125  	}
   126  
   127  	obj := reflect.New(t)
   128  
   129  	var sm Vars
   130  	var err error
   131  
   132  	if ap, ok := obj.Interface().(ArgParser); ok {
   133  		sm, err = ap.ParseArgs(s, args)
   134  	} else {
   135  		sm, err = ParseSimpleMap(s, args)
   136  	}
   137  
   138  	if err != nil {
   139  		return nil, nil, err
   140  	}
   141  
   142  	for ik, iv := range task.Vars {
   143  		if str, ok := iv.Read().(string); ok {
   144  			exp, err := ExpandVars(s, str)
   145  			if err != nil {
   146  				return nil, nil, err
   147  			}
   148  
   149  			sm[ik] = Any(exp)
   150  		} else {
   151  			sm[ik] = iv
   152  		}
   153  	}
   154  
   155  	e := obj.Elem()
   156  
   157  	for i := 0; i < t.NumField(); i++ {
   158  		f := t.Field(i)
   159  
   160  		name := strings.ToLower(f.Name)
   161  		required := false
   162  
   163  		parts := strings.Split(f.Tag.Get("tachyon"), ",")
   164  
   165  		switch len(parts) {
   166  		case 0:
   167  			// nothing
   168  		case 1:
   169  			name = parts[0]
   170  		case 2:
   171  			name = parts[0]
   172  			switch parts[1] {
   173  			case "required":
   174  				required = true
   175  			default:
   176  				return nil, nil, fmt.Errorf("Unsupported tag flag: %s", parts[1])
   177  			}
   178  		}
   179  
   180  		if val, ok := sm[name]; ok {
   181  			ef := e.Field(i)
   182  
   183  			switch ef.Interface().(type) {
   184  			case bool:
   185  				ef.Set(reflect.ValueOf(val.Read()))
   186  			case map[string]string:
   187  				iv := val.Read()
   188  				m := make(map[string]string)
   189  
   190  				switch iv := iv.(type) {
   191  				case map[interface{}]interface{}:
   192  					for k, v := range iv {
   193  						m[fmt.Sprintf("%v", k)] = fmt.Sprintf("%v", v)
   194  					}
   195  				case map[string]interface{}:
   196  					for k, v := range iv {
   197  						m[k] = fmt.Sprintf("%v", v)
   198  					}
   199  				case map[string]string:
   200  					m = iv
   201  				}
   202  
   203  				ef.Set(reflect.ValueOf(m))
   204  			default:
   205  				val := fmt.Sprintf("%v", val.Read())
   206  				enum := f.Tag.Get("enum")
   207  				if enum != "" {
   208  					found := false
   209  
   210  					for _, p := range strings.Split(enum, ",") {
   211  						if p == val {
   212  							found = true
   213  							break
   214  						}
   215  					}
   216  
   217  					if !found {
   218  						return nil, nil, fmt.Errorf("Invalid value '%s' for variable '%s'. Possibles: %s", val, name, enum)
   219  					}
   220  				}
   221  
   222  				ef.Set(reflect.ValueOf(val))
   223  			}
   224  		} else if required {
   225  			return nil, nil, fmt.Errorf("Missing value for %s", f.Name)
   226  		}
   227  	}
   228  
   229  	return obj.Interface().(Command), sm, nil
   230  }