github.com/solo-io/cue@v0.4.7/encoding/gocode/gocodec/codec.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 codec converts Go to and from CUE and validates Go values based on 16 // CUE constraints. 17 // 18 // CUE constraints can be used to validate Go types as well as fill out 19 // missing struct fields that are implied from the constraints and the values 20 // already defined by the struct value. 21 package gocodec 22 23 import ( 24 "sync" 25 26 "github.com/solo-io/cue/cue" 27 "github.com/solo-io/cue/cue/cuecontext" 28 "github.com/solo-io/cue/internal/value" 29 ) 30 31 // Config has no options yet, but is defined for future extensibility. 32 type Config struct { 33 } 34 35 // A Codec decodes and encodes CUE from and to Go values and validates and 36 // completes Go values based on CUE templates. 37 type Codec struct { 38 runtime *cue.Context 39 mutex sync.RWMutex 40 } 41 42 // New creates a new Codec for the given instance. 43 // 44 // It is safe to use the methods of Codec concurrently as long as the given 45 // Runtime is not used elsewhere while using Codec. However, only the concurrent 46 // use of Decode, Validate, and Complete is efficient. 47 func New(r *cue.Runtime, c *Config) *Codec { 48 return &Codec{runtime: value.ConvertToContext(r)} 49 } 50 51 // ExtractType extracts a CUE value from a Go type. 52 // 53 // The type represented by x is converted as the underlying type. Specific 54 // values, such as map or slice elements or field values of structs are ignored. 55 // If x is of type reflect.Type, the type represented by x is extracted. 56 // 57 // Fields of structs can be annoted using additional constrains using the 'cue' 58 // field tag. The value of the tag is a CUE expression, which may contain 59 // references to the JSON name of other fields in a struct. 60 // 61 // type Sum struct { 62 // A int `cue:"c-b" json:"a,omitempty"` 63 // B int `cue:"c-a" json:"b,omitempty"` 64 // C int `cue:"a+b" json:"c,omitempty"` 65 // } 66 // 67 func (c *Codec) ExtractType(x interface{}) (cue.Value, error) { 68 // ExtractType cannot introduce new fields on repeated calls. We could 69 // consider optimizing the lock usage based on this property. 70 c.mutex.Lock() 71 defer c.mutex.Unlock() 72 73 return fromGoType(c.runtime, x) 74 } 75 76 // TODO: allow extracting constraints and type info separately? 77 78 // Decode converts x to a CUE value. 79 // 80 // If x is of type reflect.Value it will convert the value represented by x. 81 func (c *Codec) Decode(x interface{}) (cue.Value, error) { 82 c.mutex.Lock() 83 defer c.mutex.Unlock() 84 85 // Depending on the type, can introduce new labels on repeated calls. 86 return fromGoValue(c.runtime, x, false) 87 } 88 89 // Encode converts v to a Go value. 90 func (c *Codec) Encode(v cue.Value, x interface{}) error { 91 c.mutex.RLock() 92 defer c.mutex.RUnlock() 93 94 return v.Decode(x) 95 } 96 97 var defaultCodec = New(value.ConvertToRuntime(cuecontext.New()), nil) 98 99 // Validate calls Validate on a default Codec for the type of x. 100 func Validate(x interface{}) error { 101 c := defaultCodec 102 c.mutex.RLock() 103 defer c.mutex.RUnlock() 104 105 r := defaultCodec.runtime 106 v, err := fromGoType(r, x) 107 if err != nil { 108 return err 109 } 110 w, err := fromGoValue(r, x, false) 111 if err != nil { 112 return err 113 } 114 v = v.Unify(w) 115 if err := v.Validate(); err != nil { 116 return err 117 } 118 return nil 119 } 120 121 // Validate checks whether x satisfies the constraints defined by v. 122 // 123 // The given value must be created using the same Runtime with which c was 124 // initialized. 125 func (c *Codec) Validate(v cue.Value, x interface{}) error { 126 c.mutex.RLock() 127 defer c.mutex.RUnlock() 128 129 r := checkAndForkContext(c.runtime, v) 130 w, err := fromGoValue(r, x, false) 131 if err != nil { 132 return err 133 } 134 return w.Unify(v).Err() 135 } 136 137 // Complete sets previously undefined values in x that can be uniquely 138 // determined form the constraints defined by v if validation passes, or returns 139 // an error, without modifying anything, otherwise. 140 // 141 // Only undefined values are modified. A value is considered undefined if it is 142 // pointer type and is nil or if it is a field with a zero value that has a json 143 // tag with the omitempty flag. 144 // 145 // The given value must be created using the same Runtime with which c was 146 // initialized. 147 // 148 // Complete does a JSON round trip. This means that data not preserved in such a 149 // round trip, such as the location name of a time.Time, is lost after a 150 // successful update. 151 func (c *Codec) Complete(v cue.Value, x interface{}) error { 152 c.mutex.RLock() 153 defer c.mutex.RUnlock() 154 155 r := checkAndForkContext(c.runtime, v) 156 w, err := fromGoValue(r, x, true) 157 if err != nil { 158 return err 159 } 160 161 w = w.Unify(v) 162 if err := w.Validate(cue.Concrete(true)); err != nil { 163 return err 164 } 165 return w.Decode(x) 166 } 167 168 func fromGoValue(r *cue.Context, x interface{}, allowDefault bool) (cue.Value, error) { 169 v := value.FromGoValue(r, x, allowDefault) 170 if err := v.Err(); err != nil { 171 return v, err 172 } 173 return v, nil 174 } 175 176 func fromGoType(r *cue.Context, x interface{}) (cue.Value, error) { 177 v := value.FromGoType(r, x) 178 if err := v.Err(); err != nil { 179 return v, err 180 } 181 return v, nil 182 } 183 184 func checkAndForkContext(r *cue.Context, v cue.Value) *cue.Context { 185 rr := v.Context() 186 if r != rr { 187 panic("value not from same runtime") 188 } 189 return rr 190 }