github.com/joomcode/cue@v0.4.4-0.20221111115225-539fe3512047/internal/task/task.go (about) 1 // Copyright 2019 CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package task provides a registry for tasks to be used by commands. 16 package task 17 18 import ( 19 "context" 20 "io" 21 "sync" 22 23 "github.com/joomcode/cue/cue" 24 "github.com/joomcode/cue/cue/errors" 25 "github.com/joomcode/cue/cue/token" 26 "github.com/joomcode/cue/internal/value" 27 ) 28 29 // A Context provides context for running a task. 30 type Context struct { 31 Context context.Context 32 Stdin io.Reader 33 Stdout io.Writer 34 Stderr io.Writer 35 Obj cue.Value 36 Err errors.Error 37 } 38 39 func (c *Context) Lookup(field string) cue.Value { 40 f := c.Obj.Lookup(field) 41 if !f.Exists() { 42 c.addErr(f, nil, "could not find field %q", field) 43 return cue.Value{} 44 } 45 if err := f.Err(); err != nil { 46 c.Err = errors.Append(c.Err, errors.Promote(err, "lookup")) 47 } 48 return f 49 } 50 51 func (c *Context) Int64(field string) int64 { 52 f := c.Obj.Lookup(field) 53 value, err := f.Int64() 54 if err != nil { 55 c.addErr(f, err, "invalid integer argument") 56 return 0 57 } 58 return value 59 } 60 61 func (c *Context) String(field string) string { 62 f := c.Obj.Lookup(field) 63 value, err := f.String() 64 if err != nil { 65 c.addErr(f, err, "invalid string argument") 66 return "" 67 } 68 return value 69 } 70 71 func (c *Context) Bytes(field string) []byte { 72 f := c.Obj.Lookup(field) 73 value, err := f.Bytes() 74 if err != nil { 75 c.addErr(f, err, "invalid bytes argument") 76 return nil 77 } 78 return value 79 } 80 81 func (c *Context) addErr(v cue.Value, wrap error, format string, args ...interface{}) { 82 83 err := &taskError{ 84 task: c.Obj, 85 v: v, 86 Message: errors.NewMessage(format, args), 87 } 88 c.Err = errors.Append(c.Err, errors.Wrap(err, wrap)) 89 } 90 91 // taskError wraps some error values to retain position information about the 92 // error. 93 type taskError struct { 94 task cue.Value 95 v cue.Value 96 errors.Message 97 } 98 99 var _ errors.Error = &taskError{} 100 101 func (t *taskError) Path() (a []string) { 102 for _, x := range t.v.Path().Selectors() { 103 a = append(a, x.String()) 104 } 105 return a 106 } 107 108 func (t *taskError) Position() token.Pos { 109 return t.task.Pos() 110 } 111 112 func (t *taskError) InputPositions() (a []token.Pos) { 113 _, nx := value.ToInternal(t.v) 114 115 for _, x := range nx.Conjuncts { 116 if src := x.Source(); src != nil { 117 a = append(a, src.Pos()) 118 } 119 } 120 return a 121 } 122 123 // A RunnerFunc creates a Runner. 124 type RunnerFunc func(v cue.Value) (Runner, error) 125 126 // A Runner defines a command type. 127 type Runner interface { 128 // Init is called with the original configuration before any task is run. 129 // As a result, the configuration may be incomplete, but allows some 130 // validation before tasks are kicked off. 131 // Init(v cue.Value) 132 133 // Runner runs given the current value and returns a new value which is to 134 // be unified with the original result. 135 Run(ctx *Context) (results interface{}, err error) 136 } 137 138 // Register registers a task for cue commands. 139 func Register(key string, f RunnerFunc) { 140 runners.Store(key, f) 141 } 142 143 // Lookup returns the RunnerFunc for a key. 144 func Lookup(key string) RunnerFunc { 145 v, ok := runners.Load(key) 146 if !ok { 147 return nil 148 } 149 return v.(RunnerFunc) 150 } 151 152 var runners sync.Map