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 15package cmd 16 17import ( 18 "fmt" 19 20 "github.com/spf13/cobra" 21 22 "cuelang.org/go/cue" 23 "cuelang.org/go/cue/build" 24 "cuelang.org/go/cue/format" 25 "cuelang.org/go/internal" 26 "cuelang.org/go/internal/encoding" 27 "cuelang.org/go/internal/filetypes" 28) 29 30// newEvalCmd creates a new eval command 31func newEvalCmd(c *Command) *cobra.Command { 32 cmd := &cobra.Command{ 33 Use: "eval", 34 Short: "evaluate and print a configuration", 35 Long: `eval evaluates, validates, and prints a configuration. 36 37Printing is skipped if validation fails. 38 39The --expression flag is used to evaluate an expression within the 40configuration file, instead of the entire configuration file itself. 41 42Examples: 43 44 $ cat <<EOF > foo.cue 45 a: [ "a", "b", "c" ] 46 EOF 47 48 $ cue eval foo.cue -e a[0] -e a[2] 49 "a" 50 "c" 51`, 52 RunE: mkRunE(c, runEval), 53 } 54 55 addOutFlags(cmd.Flags(), true) 56 addOrphanFlags(cmd.Flags()) 57 addInjectionFlags(cmd.Flags(), false) 58 59 cmd.Flags().StringArrayP(string(flagExpression), "e", nil, "evaluate this expression only") 60 61 cmd.Flags().BoolP(string(flagConcrete), "c", false, 62 "require the evaluation to be concrete") 63 64 cmd.Flags().BoolP(string(flagHidden), "H", false, 65 "display hidden fields") 66 67 cmd.Flags().BoolP(string(flagOptional), "O", false, 68 "display optional fields") 69 70 cmd.Flags().BoolP(string(flagAttributes), "A", false, 71 "display field attributes") 72 73 cmd.Flags().BoolP(string(flagAll), "a", false, 74 "show optional and hidden fields") 75 76 // TODO: Option to include comments in output. 77 return cmd 78} 79 80const ( 81 flagConcrete flagName = "concrete" 82 flagHidden flagName = "show-hidden" 83 flagOptional flagName = "show-optional" 84 flagAttributes flagName = "show-attributes" 85) 86 87func runEval(cmd *Command, args []string) error { 88 b, err := parseArgs(cmd, args, &config{outMode: filetypes.Eval}) 89 exitOnErr(cmd, err, true) 90 91 syn := []cue.Option{ 92 cue.Final(), // for backwards compatibility 93 cue.Definitions(true), 94 cue.Attributes(flagAttributes.Bool(cmd)), 95 cue.Optional(flagAll.Bool(cmd) || flagOptional.Bool(cmd)), 96 } 97 98 // Keep for legacy reasons. Note that `cue eval` is to be deprecated by 99 // `cue` eventually. 100 opts := []format.Option{ 101 format.UseSpaces(4), 102 format.TabIndent(false), 103 } 104 if flagSimplify.Bool(cmd) { 105 opts = append(opts, format.Simplify()) 106 } 107 b.encConfig.Format = opts 108 109 e, err := encoding.NewEncoder(b.outFile, b.encConfig) 110 exitOnErr(cmd, err, true) 111 112 iter := b.instances() 113 defer iter.close() 114 for i := 0; iter.scan(); i++ { 115 id := "" 116 if len(b.insts) > 1 { 117 id = iter.id() 118 } 119 v := iter.value() 120 121 errHeader := func() { 122 if id != "" { 123 fmt.Fprintf(cmd.OutOrStderr(), "// %s\n", id) 124 } 125 } 126 if b.outFile.Encoding != build.CUE { 127 err := e.Encode(v) 128 if err != nil { 129 errHeader() 130 exitOnErr(cmd, err, false) 131 } 132 continue 133 } 134 135 if flagConcrete.Bool(cmd) { 136 syn = append(syn, cue.Concrete(true)) 137 } 138 if flagHidden.Bool(cmd) || flagAll.Bool(cmd) { 139 syn = append(syn, cue.Hidden(true)) 140 } 141 142 if len(b.expressions) > 1 { 143 b, _ := format.Node(b.expressions[i%len(b.expressions)]) 144 id = string(b) 145 } 146 if err := v.Err(); err != nil { 147 errHeader() 148 return err 149 } 150 151 // TODO(#553): this can be removed once v.Syntax() below retains line 152 // information. 153 if (e.IsConcrete() || flagConcrete.Bool(cmd)) && !flagIgnore.Bool(cmd) { 154 if err := v.Validate(cue.Concrete(true)); err != nil { 155 errHeader() 156 exitOnErr(cmd, err, false) 157 continue 158 } 159 } 160 161 f := internal.ToFile(v.Syntax(syn...)) 162 f.Filename = id 163 err := e.EncodeFile(f) 164 if err != nil { 165 errHeader() 166 exitOnErr(cmd, err, false) 167 } 168 } 169 exitOnErr(cmd, iter.err(), true) 170 171 err = e.Close() 172 exitOnErr(cmd, err, true) 173 174 return nil 175} 176