// Copyright 2021 CUE Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cue import ( "bytes" "fmt" "math/big" "cuelang.org/go/cue/ast" "cuelang.org/go/cue/format" "cuelang.org/go/internal/core/export" ) // TODO: // * allow '-' to strip outer curly braces? // - simplify output; can be used in combination with other flags // * advertise: // c like v, but print comments // a like c, but print attributes and package-local hidden fields as well // Format prints a CUE value. // // WARNING: although we are narrowing down the semantics, the verbs and options // are still subject to change. this API is experimental although it is likely // getting close to the final design. // // It recognizes the following verbs: // // v print CUE value // // The verbs support the following flags: // // # print as schema and include definitions. // The result is printed as a self-contained file, instead of an the // expression format. // + evaluate: resolve defaults and error on incomplete errors // // Indentation can be controlled as follows: // // width indent the cue block by tab stops (e.g. %2v) // precision convert tabs to spaces (e.g. %.2v), where // a value of 0 means no indentation or newlines (TODO). // // If the value kind corresponds to one of the following Go types, the // usual Go formatting verbs for that type can be used: // // Int: b,d,o,O,q,x,X // Float: f,e,E,g,G // String/Bytes: s,q,x,X // // The %v directive will be used if the type is not supported for that verb. func (v Value) Format(state fmt.State, verb rune) { if v.v == nil { fmt.Fprint(state, "") return } switch verb { case 'a': formatCUE(state, v, true, true) case 'c': formatCUE(state, v, true, false) case 'v': formatCUE(state, v, false, false) case 'd', 'o', 'O', 'U': var i big.Int if _, err := v.Int(&i); err != nil { formatCUE(state, v, false, false) return } i.Format(state, verb) case 'f', 'e', 'E', 'g', 'G': d, err := v.Decimal() if err != nil { formatCUE(state, v, false, false) return } d.Format(state, verb) case 's', 'q': // TODO: this drops other formatting directives msg := "%s" if verb == 'q' { msg = "%q" } if b, err := v.Bytes(); err == nil { fmt.Fprintf(state, msg, b) } else { s := fmt.Sprintf("%+v", v) fmt.Fprintf(state, msg, s) } case 'x', 'X': switch v.Kind() { case StringKind, BytesKind: b, _ := v.Bytes() // TODO: this drops other formatting directives msg := "%x" if verb == 'X' { msg = "%X" } fmt.Fprintf(state, msg, b) case IntKind, NumberKind: var i big.Int _, _ = v.Int(&i) i.Format(state, verb) case FloatKind: dec, _ := v.Decimal() dec.Format(state, verb) default: formatCUE(state, v, false, false) } default: formatCUE(state, v, false, false) } } func formatCUE(state fmt.State, v Value, showDocs, showAll bool) { pkgPath := v.instance().ID() p := *export.Simplified isDef := false switch { case state.Flag('#'): isDef = true p = export.Profile{ ShowOptional: true, ShowDefinitions: true, ShowHidden: true, } case state.Flag('+'): p = *export.Final fallthrough default: p.ShowHidden = showAll } p.ShowDocs = showDocs p.ShowAttributes = showAll var n ast.Node if isDef { n, _ = p.Def(v.idx, pkgPath, v.v) } else { n, _ = p.Value(v.idx, pkgPath, v.v) } formatExpr(state, n) } func formatExpr(state fmt.State, n ast.Node) { opts := make([]format.Option, 0, 3) if state.Flag('-') { opts = append(opts, format.Simplify()) } // TODO: handle verbs to allow formatting based on type: if width, ok := state.Width(); ok { opts = append(opts, format.IndentPrefix(width)) } // TODO: consider this: should tabs or spaces be the default? if tabwidth, ok := state.Precision(); ok { // TODO: 0 means no newlines. opts = append(opts, format.UseSpaces(tabwidth), format.TabIndent(false)) } // TODO: consider this. // else if state.Flag(' ') { // opts = append(opts, // format.UseSpaces(4), // format.TabIndent(false)) // } b, _ := format.Node(n, opts...) b = bytes.Trim(b, "\n\r") _, _ = state.Write(b) }