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 }