cuelang.org/go@v0.10.1/cue/format.go (about) 1 // Copyright 2021 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 cue 16 17 import ( 18 "bytes" 19 "fmt" 20 "math/big" 21 22 "cuelang.org/go/cue/ast" 23 "cuelang.org/go/cue/format" 24 "cuelang.org/go/internal/core/export" 25 ) 26 27 // TODO: 28 // * allow '-' to strip outer curly braces? 29 // - simplify output; can be used in combination with other flags 30 // * advertise: 31 // c like v, but print comments 32 // a like c, but print attributes and package-local hidden fields as well 33 34 // Format prints a CUE value. 35 // 36 // WARNING: although we are narrowing down the semantics, the verbs and options 37 // are still subject to change. this API is experimental although it is likely 38 // getting close to the final design. 39 // 40 // It recognizes the following verbs: 41 // 42 // v print CUE value 43 // 44 // The verbs support the following flags: 45 // 46 // # print as schema and include definitions. 47 // The result is printed as a self-contained file, instead of an the 48 // expression format. 49 // + evaluate: resolve defaults and error on incomplete errors 50 // 51 // Indentation can be controlled as follows: 52 // 53 // width indent the cue block by <width> tab stops (e.g. %2v) 54 // precision convert tabs to <precision> spaces (e.g. %.2v), where 55 // a value of 0 means no indentation or newlines (TODO). 56 // 57 // If the value kind corresponds to one of the following Go types, the 58 // usual Go formatting verbs for that type can be used: 59 // 60 // Int: b,d,o,O,q,x,X 61 // Float: f,e,E,g,G 62 // String/Bytes: s,q,x,X 63 // 64 // The %v directive will be used if the type is not supported for that verb. 65 func (v Value) Format(state fmt.State, verb rune) { 66 if v.v == nil { 67 fmt.Fprint(state, "<nil>") 68 return 69 } 70 71 switch verb { 72 case 'a': 73 formatCUE(state, v, true, true) 74 case 'c': 75 formatCUE(state, v, true, false) 76 case 'v': 77 formatCUE(state, v, false, false) 78 79 case 'd', 'o', 'O', 'U': 80 var i big.Int 81 if _, err := v.Int(&i); err != nil { 82 formatCUE(state, v, false, false) 83 return 84 } 85 i.Format(state, verb) 86 87 case 'f', 'e', 'E', 'g', 'G': 88 d, err := v.Decimal() 89 if err != nil { 90 formatCUE(state, v, false, false) 91 return 92 } 93 d.Format(state, verb) 94 95 case 's', 'q': 96 // TODO: this drops other formatting directives 97 msg := "%s" 98 if verb == 'q' { 99 msg = "%q" 100 } 101 102 if b, err := v.Bytes(); err == nil { 103 fmt.Fprintf(state, msg, b) 104 } else { 105 s := fmt.Sprintf("%+v", v) 106 fmt.Fprintf(state, msg, s) 107 } 108 109 case 'x', 'X': 110 switch v.Kind() { 111 case StringKind, BytesKind: 112 b, _ := v.Bytes() 113 // TODO: this drops other formatting directives 114 msg := "%x" 115 if verb == 'X' { 116 msg = "%X" 117 } 118 fmt.Fprintf(state, msg, b) 119 120 case IntKind, NumberKind: 121 var i big.Int 122 _, _ = v.Int(&i) 123 i.Format(state, verb) 124 125 case FloatKind: 126 dec, _ := v.Decimal() 127 dec.Format(state, verb) 128 129 default: 130 formatCUE(state, v, false, false) 131 } 132 133 default: 134 formatCUE(state, v, false, false) 135 } 136 } 137 138 func formatCUE(state fmt.State, v Value, showDocs, showAll bool) { 139 140 pkgPath := v.instance().ID() 141 142 p := *export.Simplified 143 144 isDef := false 145 switch { 146 case state.Flag('#'): 147 isDef = true 148 p = export.Profile{ 149 ShowOptional: true, 150 ShowDefinitions: true, 151 ShowHidden: true, 152 } 153 154 case state.Flag('+'): 155 p = *export.Final 156 fallthrough 157 158 default: 159 p.ShowHidden = showAll 160 } 161 162 p.ShowDocs = showDocs 163 p.ShowAttributes = showAll 164 165 var n ast.Node 166 if isDef { 167 n, _ = p.Def(v.idx, pkgPath, v.v) 168 } else { 169 n, _ = p.Value(v.idx, pkgPath, v.v) 170 } 171 172 formatExpr(state, n) 173 } 174 175 func formatExpr(state fmt.State, n ast.Node) { 176 opts := make([]format.Option, 0, 3) 177 if state.Flag('-') { 178 opts = append(opts, format.Simplify()) 179 } 180 // TODO: handle verbs to allow formatting based on type: 181 if width, ok := state.Width(); ok { 182 opts = append(opts, format.IndentPrefix(width)) 183 } 184 // TODO: consider this: should tabs or spaces be the default? 185 if tabwidth, ok := state.Precision(); ok { 186 // TODO: 0 means no newlines. 187 opts = append(opts, 188 format.UseSpaces(tabwidth), 189 format.TabIndent(false)) 190 } 191 // TODO: consider this. 192 // else if state.Flag(' ') { 193 // opts = append(opts, 194 // format.UseSpaces(4), 195 // format.TabIndent(false)) 196 // } 197 198 b, _ := format.Node(n, opts...) 199 b = bytes.Trim(b, "\n\r") 200 _, _ = state.Write(b) 201 }