github.com/joomcode/cue@v0.4.4-0.20221111115225-539fe3512047/pkg/encoding/yaml/manual.go (about) 1 // Copyright 2018 The 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 yaml 16 17 import ( 18 "bytes" 19 "io" 20 21 "github.com/joomcode/cue/cue" 22 "github.com/joomcode/cue/cue/ast" 23 "github.com/joomcode/cue/internal" 24 cueyaml "github.com/joomcode/cue/internal/encoding/yaml" 25 "github.com/joomcode/cue/internal/third_party/yaml" 26 ) 27 28 // Marshal returns the YAML encoding of v. 29 func Marshal(v cue.Value) (string, error) { 30 if err := v.Validate(cue.Concrete(true)); err != nil { 31 if err := v.Validate(); err != nil { 32 return "", err 33 } 34 // TODO: allow adt.Bottom to implement errors.Error so that code and 35 // messages can be passed. 36 return "", internal.ErrIncomplete 37 } 38 n := v.Syntax(cue.Final(), cue.Concrete(true)) 39 b, err := cueyaml.Encode(n) 40 return string(b), err 41 } 42 43 // MarshalStream returns the YAML encoding of v. 44 func MarshalStream(v cue.Value) (string, error) { 45 // TODO: return an io.Reader and allow asynchronous processing. 46 iter, err := v.List() 47 if err != nil { 48 return "", err 49 } 50 buf := &bytes.Buffer{} 51 for i := 0; iter.Next(); i++ { 52 if i > 0 { 53 buf.WriteString("---\n") 54 } 55 v := iter.Value() 56 if err := v.Validate(cue.Concrete(true)); err != nil { 57 if err := v.Validate(); err != nil { 58 return "", err 59 } 60 // TODO: allow adt.Bottom to implement errors.Error so that code and 61 // messages can be passed. 62 return "", internal.ErrIncomplete 63 } 64 n := v.Syntax(cue.Final(), cue.Concrete(true)) 65 b, err := cueyaml.Encode(n) 66 if err != nil { 67 return "", err 68 } 69 buf.Write(b) 70 } 71 return buf.String(), nil 72 } 73 74 // Unmarshal parses the YAML to a CUE expression. 75 func Unmarshal(data []byte) (ast.Expr, error) { 76 return yaml.Unmarshal("", data) 77 } 78 79 // UnmarshalStream parses the YAML to a CUE list expression on success. 80 func UnmarshalStream(data []byte) (ast.Expr, error) { 81 d, err := yaml.NewDecoder("", data) 82 if err != nil { 83 return nil, err 84 } 85 86 a := []ast.Expr{} 87 for { 88 x, err := d.Decode() 89 if err == io.EOF { 90 break 91 } 92 if err != nil { 93 return nil, err 94 } 95 a = append(a, x) 96 } 97 98 return ast.NewList(a...), nil 99 } 100 101 // Validate validates YAML and confirms it is an instance of the schema 102 // specified by v. If the YAML source is a stream, every object must match v. 103 func Validate(b []byte, v cue.Value) (bool, error) { 104 d, err := yaml.NewDecoder("yaml.Validate", b) 105 if err != nil { 106 return false, err 107 } 108 r := v.Context() 109 for { 110 expr, err := d.Decode() 111 if err != nil { 112 if err == io.EOF { 113 return true, nil 114 } 115 return false, err 116 } 117 118 x := r.BuildExpr(expr) 119 if err := x.Err(); err != nil { 120 return false, err 121 } 122 123 // TODO: consider using subsumption again here. 124 // Alternatives: 125 // - allow definition of non-concrete list, 126 // like list.Of(int), or []int. 127 // - Introduce ! in addition to ?, allowing: 128 // list!: [...] 129 // if err := v.Subsume(inst.Value(), cue.Final()); err != nil { 130 // return false, err 131 // } 132 x = v.Unify(x) 133 if err := x.Err(); err != nil { 134 return false, err 135 } 136 if err := x.Validate(cue.Concrete(true)); err != nil { 137 return false, err 138 } 139 140 } 141 } 142 143 // ValidatePartial validates YAML and confirms it matches the constraints 144 // specified by v using unification. This means that b must be consistent with, 145 // but does not have to be an instance of v. If the YAML source is a stream, 146 // every object must match v. 147 func ValidatePartial(b []byte, v cue.Value) (bool, error) { 148 d, err := yaml.NewDecoder("yaml.ValidatePartial", b) 149 if err != nil { 150 return false, err 151 } 152 r := v.Context() 153 for { 154 expr, err := d.Decode() 155 if err != nil { 156 if err == io.EOF { 157 return true, nil 158 } 159 return false, err 160 } 161 162 x := r.BuildExpr(expr) 163 if err := x.Err(); err != nil { 164 return false, err 165 } 166 167 if x := v.Unify(x); x.Err() != nil { 168 return false, x.Err() 169 } 170 } 171 }