diff --git a/pkg/yqlib/decoder_hcl.go b/pkg/yqlib/decoder_hcl.go index 395d8508..6542ebc5 100644 --- a/pkg/yqlib/decoder_hcl.go +++ b/pkg/yqlib/decoder_hcl.go @@ -3,6 +3,7 @@ package yqlib import ( "fmt" "io" + "math/big" "strconv" "strings" @@ -112,11 +113,16 @@ func convertHclExprToNode(expr hclsyntax.Expression, src []byte) *CandidateNode b := v.True() return createScalarNode(b, strconv.FormatBool(b)) case v.Type() == cty.Number: - // represent numbers as float string + // prefer integers when the numeric value is integral bf := v.AsBigFloat() if bf == nil { // fallback to string - return createScalarNode(nil, v.GoString()) + return createStringScalarNode(v.GoString()) + } + // check if bf represents an exact integer + if intVal, acc := bf.Int(nil); acc == big.Exact { + s := intVal.String() + return createScalarNode(int64(intVal.Int64()), s) } s := bf.Text('g', -1) return createScalarNode(0.0, s) @@ -144,7 +150,7 @@ func convertHclExprToNode(expr hclsyntax.Expression, src []byte) *CandidateNode default: // fallback to string s := v.GoString() - return createScalarNode(nil, s) + return createStringScalarNode(s) } case *hclsyntax.TemplateExpr: // join parts; if single literal, return that string @@ -177,9 +183,9 @@ func convertHclExprToNode(expr hclsyntax.Expression, src []byte) *CandidateNode end := r.End.Byte if start > 0 && end >= start && end <= len(src) { text := string(src[start-1 : end]) - return createScalarNode(nil, text) + return createStringScalarNode(text) } - return createScalarNode(nil, fmt.Sprintf("%v", expr)) + return createStringScalarNode(fmt.Sprintf("%v", expr)) } } @@ -196,7 +202,11 @@ func convertCtyValueToNode(v cty.Value) *CandidateNode { case v.Type() == cty.Number: bf := v.AsBigFloat() if bf == nil { - return createScalarNode(nil, v.GoString()) + return createStringScalarNode(v.GoString()) + } + if intVal, acc := bf.Int(nil); acc == big.Exact { + s := intVal.String() + return createScalarNode(int64(intVal.Int64()), s) } s := bf.Text('g', -1) return createScalarNode(0.0, s) @@ -219,7 +229,7 @@ func convertCtyValueToNode(v cty.Value) *CandidateNode { } return m default: - return createScalarNode(nil, v.GoString()) + return createStringScalarNode(v.GoString()) } } diff --git a/pkg/yqlib/encoder_yaml.go b/pkg/yqlib/encoder_yaml.go index afae87b0..607361d3 100644 --- a/pkg/yqlib/encoder_yaml.go +++ b/pkg/yqlib/encoder_yaml.go @@ -5,6 +5,7 @@ import ( "bytes" "errors" "io" + "strings" "github.com/fatih/color" diff --git a/pkg/yqlib/hcl_test.go b/pkg/yqlib/hcl_test.go index 490e7c25..882a2d11 100644 --- a/pkg/yqlib/hcl_test.go +++ b/pkg/yqlib/hcl_test.go @@ -13,6 +13,42 @@ var hclFormatScenarios = []formatScenario{ expected: "io_mode: async\n", scenarioType: "decode", }, + { + description: "number attribute", + input: `port = 8080`, + expected: "port: 8080\n", + scenarioType: "decode", + }, + { + description: "float attribute", + input: `pi = 3.14`, + expected: "pi: 3.14\n", + scenarioType: "decode", + }, + { + description: "boolean attribute", + input: `enabled = true`, + expected: "enabled: true\n", + scenarioType: "decode", + }, + { + description: "list of strings", + input: `tags = ["a", "b"]`, + expected: "tags: ' [\"a\", \"b\"]'\n", + scenarioType: "decode", + }, + { + description: "object/map attribute", + input: `obj = { a = 1, b = "two" }`, + expected: "obj: ' { a = 1, b = \"two\" }'\n", + scenarioType: "decode", + }, + { + description: "nested block", + input: `server { port = 8080 }`, + expected: "server:\n port: 8080\n", + scenarioType: "decode", + }, } func testHclScenario(t *testing.T, s formatScenario) {