diff --git a/cmd/strucmd/main.go b/cmd/strucmd/main.go deleted file mode 100644 index aa46a6d5..00000000 --- a/cmd/strucmd/main.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -import ( - "fmt" -) - -func main() { - fmt.Println("NOT IMPLEMENTED YET") -} diff --git a/dynamicstruct/decoder/benchmark_test.go b/dynamicstruct/decoder/benchmark_test.go index 18651cb8..cb2e6e2b 100644 --- a/dynamicstruct/decoder/benchmark_test.go +++ b/dynamicstruct/decoder/benchmark_test.go @@ -6,6 +6,310 @@ import ( . "github.com/goldeneggg/structil/dynamicstruct/decoder" ) +var ( + singleJSON = []byte(` +{ + "null_field":null, + "string_field":"かきくけこ", + "int_field":45678, + "float64_field":9.876, + "bool_field":false, + "struct_ptr_field":{ + "key":"hugakey", + "value":"hugavalue" + }, + "array_string_field":[ + "array_str_1", + "array_str_2" + ], + "array_struct_field":[ + { + "kkk":"kkk1", + "vvvv":"vvv1" + }, + { + "kkk":"kkk2", + "vvvv":"vvv2" + }, + { + "kkk":"kkk3", + "vvvv":"vvv3" + } + ] +} +`) + + arrayJSON = []byte(` +[ + { + "null_field":null, + "string_field":"かきくけこ", + "int_field":45678, + "float64_field":9.876, + "bool_field":false, + "struct_ptr_field":{ + "key":"hugakey", + "value":"hugavalue" + }, + "array_string_field":[ + "array_str_1", + "array_str_2" + ], + "array_struct_field":[ + { + "kkk":"kkk1", + "vvvv":"vvv1" + }, + { + "kkk":"kkk2", + "vvvv":"vvv2" + }, + { + "kkk":"kkk3", + "vvvv":"vvv3" + } + ] + }, + { + "null_field":null, + "string_field":"さしすせそ", + "int_field":7890, + "float64_field":4.99, + "bool_field":true, + "struct_ptr_field":{ + "key":"hugakeyXXX", + "value":"hugavalueXXX" + }, + "array_string_field":[ + "array_str_111", + "array_str_222" + ], + "array_struct_field":[ + { + "kkk":"kkk99", + "vvvv":"vvv99" + }, + { + "kkk":"kkk999", + "vvvv":"vvv999" + }, + { + "kkk":"kkk9999", + "vvvv":"vvv9999" + } + ] + } +] +`) + + singleYAML = []byte(` +null_field: null +string_field: かきくけこ +int_field: 45678 +float64_field: 9.876 +bool_field: false +struct_ptr_field: + key: hugakey + value: hugavalue +array_string_field: + - array_str_1 + - array_str_2 +array_struct_field: + - kkk: kkk1 + vvvv: vvv1 + - kkk: kkk2 + vvvv: vvv2 + - kkk: kkk3 + vvvv: vvv3 +`) + + arrayYAML = []byte(` +- null_field: null + string_field: かきくけこ + int_field: 45678 + float64_field: 9.876 + bool_field: false + struct_ptr_field: + key: hugakey + value: hugavalue + array_string_field: + - array_str_1 + - array_str_2 + array_struct_field: + - kkk: kkk1 + vvvv: vvv1 + - kkk: kkk2 + vvvv: vvv2 + - kkk: kkk3 + vvvv: vvv3 +- null_field: null + string_field: さしすせそ + int_field: 7890 + float64_field: 4.99 + bool_field: true + struct_ptr_field: + key: hugakeyXXX + value: hugavalueXXX + array_string_field: + - array_str_111 + - array_str_222 + array_struct_field: + - kkk: kkk99 + vvvv: vvv99 + - kkk: kkk999 + vvvv: vvv999 + - kkk: kkk9999 + vvvv: vvv9999 +`) + + //lint:ignore U1000 It's ok because this is for the future. + singleTOML = []byte(` +string_field = "かきくけこ," +int_field = 45678 +float64_field = "9.876," +bool_field = false +array_string_field = ["array_str_1", "array_str_2"] + +[struct_ptr_field] + key = "hugakey" + value = "hugavalue" + +[[array_struct_field]] + kkk = "kkk1" + vvvv = "vvv1" + +[[array_struct_field]] + kkk = "kkk2" + vvvv = "vvv2" + +[[array_struct_field]] + kkk = "kkk3" + vvvv = "vvv3" +`) + + //lint:ignore U1000 It's ok because this is for the future. + singleXML = []byte(` + + + + かきくけこ + 45678 + 9.876 + false + + hugakey + hugavalue + + array_str_1 + array_str_2 + + kkk1 + vvv1 + + + kkk2 + vvv2 + + + kkk3 + vvv3 + + +`) + + //lint:ignore U1000 It's ok because this is for the future. + arrayXML = []byte(` + + + <0> + + かきくけこ + 45678 + 9.876 + false + + hugakey + hugavalue + + array_str_1 + array_str_2 + + kkk1 + vvv1 + + + kkk2 + vvv2 + + + kkk3 + vvv3 + + + <1> + + さしすせそ + 7890 + 4.99 + true + + hugakeyXXX + hugavalueXXX + + array_str_111 + array_str_222 + + kkk99 + vvv99 + + + kkk999 + vvv999 + + + kkk9999 + vvv9999 + + + +`) + + //lint:ignore U1000 It's ok because this is for the future. + singleHCL = []byte(` +null_field = + +string_field = "かきくけこ," + +int_field = 45678 + +float64_field = "9.876," + +bool_field = false + +struct_ptr_field = { + key = "hugakey" + value = "hugavalue" +} + +array_string_field = ["array_str_1", "array_str_2"] + +array_struct_field = { + kkk = "kkk1" + vvvv = "vvv1" +} + +"array_struct_field" = { + kkk = "kkk2" + vvvv = "vvv2" +} + +array_struct_field = { + kkk = "kkk3" + vvvv = "vvv3" +} +`) +) + func BenchmarkFromJSON_singleJSON(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { @@ -74,6 +378,34 @@ func BenchmarkDynamicStruct_arrayJSON_nest_useTag(b *testing.B) { } } +func BenchmarkJSONToGetter_singleJSON_nonNest(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = JSONToGetter(singleJSON, false) + } +} + +func BenchmarkJSONToGetter_singleJSON_nest(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = JSONToGetter(singleJSON, true) + } +} + +func BenchmarkJSONToGetter_arrayJSON_nonNest(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = JSONToGetter(arrayJSON, false) + } +} + +func BenchmarkJSONToGetter_arrayJSON_nest(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = JSONToGetter(arrayJSON, true) + } +} + func BenchmarkFromYAML_singleYAML(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { @@ -142,34 +474,6 @@ func BenchmarkDynamicStruct_arrayYAML_nest_useTag(b *testing.B) { } } -func BenchmarkJSONToGetter_singleJSON_nonNest(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _ = JSONToGetter(singleJSON, false) - } -} - -func BenchmarkJSONToGetter_singleJSON_nest(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _ = JSONToGetter(singleJSON, true) - } -} - -func BenchmarkJSONToGetter_arrayJSON_nonNest(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _ = JSONToGetter(arrayJSON, false) - } -} - -func BenchmarkJSONToGetter_arrayJSON_nest(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _ = JSONToGetter(arrayJSON, true) - } -} - func BenchmarkYAMLToGetter_singleYAML_nonNest(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { diff --git a/dynamicstruct/decoder/data_type.go b/dynamicstruct/decoder/data_type.go index 6f6ccb3b..3f637c98 100644 --- a/dynamicstruct/decoder/data_type.go +++ b/dynamicstruct/decoder/data_type.go @@ -4,6 +4,9 @@ import ( "encoding/json" "fmt" + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hclsimple" + "github.com/zclconf/go-cty/cty" "gopkg.in/yaml.v3" ) @@ -12,21 +15,13 @@ import ( type dataType int const ( - // TypeJSON is the type sign of JSON typeJSON dataType = iota - - // TypeYAML is the type sign of YAML typeYAML + typeHCL // FIXME: futures as follows - - // TypeXML is the type sign of XML // TypeXML - - // TypeTOML is the type sign of TOML // TypeTOML - - // TypeCSV is the type sign of CSV // TypeCSV end // end of iota @@ -35,6 +30,7 @@ const ( var formats = [...]string{ typeJSON: "json", typeYAML: "yaml", + typeHCL: "hcl", } func (dt dataType) string() string { @@ -44,27 +40,101 @@ func (dt dataType) string() string { return "" } -func (dt dataType) unmarshal(data []byte) (interface{}, error) { - var intf interface{} - err := dt.unmarshalWithIPtr(data, &intf) - return intf, err -} +func (dt dataType) unmarshal(data []byte) (ret interface{}, err error) { + switch dt { + case typeHCL: + // Note: hclsimple.Decode supports only pointer of map or struct. + var m map[string]interface{} + err = hclsimple.Decode("example.hcl", data, nil, &m) + if err != nil { + return nil, err + } + dec, err := decodeHCL(data) + if err != nil { + return nil, err + } + return interface{}(dec), nil + default: + err = dt.unmarshalWithPtr(data, &ret) + } -func (dt dataType) unmarshalWithIPtr(data []byte, iptr interface{}) error { - var err error + return +} +func (dt dataType) unmarshalWithPtr(data []byte, iptr interface{}) (err error) { switch dt { case typeJSON: - // Note: iptr should be "map[string]interface{}" err = json.Unmarshal(data, iptr) case typeYAML: - // Note: iptr should be "map[interface{}]interface{}" using gopkg.in/yaml.v2 package err = yaml.Unmarshal(data, iptr) default: err = fmt.Errorf("invalid datatype for Unmarshal: %v", dt) } - return err + return +} + +func decodeHCL(data []byte) (map[string]interface{}, error) { + // Note: hclsimple.Decode supports only pointer of map or struct. + var m map[string]interface{} + err := hclsimple.Decode("example.hcl", data, nil, &m) + if err != nil { + return m, err + } + + decoded := make(map[string]interface{}, len(m)) + for k, v := range m { + attr, ok := v.(*hcl.Attribute) + if !ok { + return decoded, fmt.Errorf("%q field can not cast to *hcl.Attribute. v = %v", k, v) + } + + ctyVal, _ := attr.Expr.Value(nil) + decoded[k], err = convCtyToGo(ctyVal) + if err != nil { + return decoded, err + } + } + + return decoded, nil +} + +func convCtyToGo(ctyVal cty.Value) (interface{}, error) { + var err error + + ctyType := ctyVal.Type() + if ctyType == cty.String { + return ctyVal.AsString(), nil + } else if ctyType == cty.Number { + return ctyVal.AsBigFloat(), nil + } else if ctyType == cty.Bool { + return ctyVal.True(), nil + } else if ctyType.IsTupleType() { + vals := ctyVal.AsValueSlice() + ret := make([]interface{}, len(vals)) + for i, v := range vals { + ret[i], err = convCtyToGo(v) + if err != nil { + return nil, err + } + } + return ret, nil + } else if ctyType.IsObjectType() { + valM := ctyVal.AsValueMap() + retM := make(map[string]interface{}, len(valM)) + for k, v := range valM { + retM[k], err = convCtyToGo(v) + if err != nil { + return nil, err + } + } + return retM, nil + } else if ctyType == cty.DynamicPseudoType { + // FIXME: just support only null? + return nil, nil + } else { + return nil, fmt.Errorf("unsupported ctyType: %v", ctyType) + } } // TODO: add tests and examples @@ -72,10 +142,8 @@ func (dt dataType) unmarshalWithIPtr(data []byte, iptr interface{}) error { func (dt dataType) marshal(m map[string]interface{}) (data []byte, err error) { switch dt { case typeJSON: - // Note: v is expected to be "map[string]interface{}" data, err = json.Marshal(m) case typeYAML: - // Note: v is expected to be converted from "map[interface{}]interface{}" to "map[string]interface{}" data, err = yaml.Marshal(m) default: err = fmt.Errorf("invalid datatype for Marshal: %v", dt) diff --git a/dynamicstruct/decoder/decoder.go b/dynamicstruct/decoder/decoder.go index 02b5b66b..cdfd2881 100644 --- a/dynamicstruct/decoder/decoder.go +++ b/dynamicstruct/decoder/decoder.go @@ -2,6 +2,7 @@ package decoder import ( "fmt" + "math/big" "github.com/iancoleman/strcase" @@ -11,70 +12,66 @@ import ( // Decoder is the struct that decodes some marshaled data like JSON and YAML. type Decoder struct { - dt dataType - orgData []byte // original data - orgIntf interface{} // unmarshaled interface from original data - strKeyMap map[string]interface{} // string key map for decoding to DymanicStruct - ds *dynamicstruct.DynamicStruct - dsi interface{} // unmarshaled result from data to DynamicStruct + typ dataType + data []byte + intf interface{} + m map[string]interface{} + ds *dynamicstruct.DynamicStruct } -func newDecoder(data []byte, dt dataType) (*Decoder, error) { - unm, err := dt.unmarshal(data) +// FromJSON returns a concrete Decoder for JSON. +func FromJSON(data []byte) (*Decoder, error) { + return newDecoder(data, typeJSON) +} + +// FromYAML returns a concrete Decoder for YAML. +func FromYAML(data []byte) (*Decoder, error) { + return newDecoder(data, typeYAML) +} + +// FromHCL returns a concrete Decoder for HCL. +func FromHCL(data []byte) (*Decoder, error) { + return newDecoder(data, typeHCL) +} + +// FromXML returns a concrete Decoder for XML. +// FIXME: This function is still a future candidate (returned error now) +func FromXML(data []byte) (*Decoder, error) { + return newDecoder(data, end) // FIXME: "end" is provisional type +} + +func newDecoder(data []byte, typ dataType) (*Decoder, error) { + intf, err := typ.unmarshal(data) if err != nil { return nil, err } - dec := &Decoder{ - dt: dt, - orgData: data, - orgIntf: unm, - strKeyMap: make(map[string]interface{}), - } - - switch t := dec.orgIntf.(type) { + m := make(map[string]interface{}) + switch t := intf.(type) { case map[string]interface{}: // JSON - dec.strKeyMap = t - // Note: this is dead case with gopkg.in/yaml.v3 (but alive with v2) - // case map[interface{}]interface{}: - // // YAML - // dec.strKeyMap = toStringKeyMap(t) + m = t case []interface{}: if len(t) > 0 { // The items in the array must be same for all elements. // So the first element is used to process switch tt := t[0].(type) { case map[string]interface{}: - dec.strKeyMap = tt - // Note: this is dead case with gopkg.in/yaml.v3 (but alive with v2) - // case map[interface{}]interface{}: - // dec.strKeyMap = toStringKeyMap(tt) + m = tt default: return nil, fmt.Errorf("unexpected type of t[0] [%v]", tt) } } default: - return nil, fmt.Errorf("unexpected type of dec.orgIntf [%v]", t) + return nil, fmt.Errorf("unexpected type of unmashalled interface [%v]", t) } - return dec, nil -} - -// FromJSON returns a concrete Decoder for JSON. -func FromJSON(data []byte) (*Decoder, error) { - return newDecoder(data, typeJSON) -} - -// FromYAML returns a concrete Decoder for YAML. -func FromYAML(data []byte) (*Decoder, error) { - return newDecoder(data, typeYAML) -} - -// FromXML returns a concrete Decoder for XML. -// FIXME: This function is still a future candidate (returned error now) -func FromXML(data []byte) (*Decoder, error) { - return newDecoder(data, end) // FIXME: "end" is provisional type + return &Decoder{ + typ: typ, + data: data, + intf: intf, + m: m, + }, nil } // JSONToGetter returns a structil.Getter with a decoded JSON via DynamicStruct. @@ -97,6 +94,28 @@ func YAMLToGetter(data []byte, nest bool) (*structil.Getter, error) { return d.dsToGetter(nest) } +// FIXME: HCL decode method supports only struct pointer or map pointer +// func HCLToGetter(data []byte, nest bool) (*structil.Getter, error) { +// d, err := FromHCL(data) +// if err != nil { +// return nil, fmt.Errorf("fail to HCLToGetter: %w", err) +// } + +// return d.dsToGetter(nest) +// } + +// DynamicStruct returns a decoded DynamicStruct with unmarshaling data to DynamicStruct interface. +func (d *Decoder) DynamicStruct(nest bool, useTag bool) (*dynamicstruct.DynamicStruct, error) { + var err error + + d.ds, err = d.toDsFromStringMap(d.m, nest, useTag) + if err != nil { + return nil, err + } + + return d.ds, err +} + func (d *Decoder) dsToGetter(nest bool) (*structil.Getter, error) { ds, err := d.DynamicStruct(nest, true) if err != nil { @@ -112,50 +131,20 @@ func (d *Decoder) dsToGetter(nest bool) (*structil.Getter, error) { } func (d *Decoder) decodeToDynamicStruct(ds *dynamicstruct.DynamicStruct) (interface{}, error) { - // toDsFromStringMap() method uses "Build()" method and this means that ds is build by pointer-mode - // So ds.NewInterface() returns a struct *pointer* - d.dsi = ds.NewInterface() - - // must use "d.strKeyMap" (not "d.orgIntf"). because key of "d.orgIntf" is not string but interface{} - data, err := d.dt.marshal(d.strKeyMap) + data, err := d.typ.marshal(d.m) if err != nil { - return nil, fmt.Errorf("fail to d.dt.marshal: %w", err) - } - - // must use "d.dsi" (not "&d.dsi"). because "d.dsi" is pointer - // if use "&.d.dsi", unmarshal result is not struct but map[interface{}]interface when dt is YAML - if err := d.dt.unmarshalWithIPtr(data, d.dsi); err != nil { - return nil, fmt.Errorf("fail to d.dt.unmarshalWithIPtr: %w", err) + return nil, fmt.Errorf("fail to d.typ.marshal: %w", err) } - return d.dsi, nil -} - -// OrgData returns an original data as []byte. -func (d *Decoder) OrgData() []byte { - return d.orgData -} - -// DynamicStruct returns a decoded DynamicStruct with unmarshaling data to DynamicStruct interface. -func (d *Decoder) DynamicStruct(nest bool, useTag bool) (*dynamicstruct.DynamicStruct, error) { - var err error - - // d.ds, err = d.toDs(d.orgIntf, nest, useTag) - d.ds, err = d.toDs(d.strKeyMap, nest, useTag) + // ds.NewInterface() returns a struct *pointer* + dsi := ds.NewInterface() + // must use "dsi" (not "&dsi"). because "dsi" is pointer + err = d.typ.unmarshalWithPtr(data, dsi) if err != nil { - return nil, err - } - - return d.ds, err -} - -func (d *Decoder) toDs(i interface{}, nest bool, useTag bool) (*dynamicstruct.DynamicStruct, error) { - switch t := i.(type) { - case map[string]interface{}: - return d.toDsFromStringMap(t, nest, useTag) + return nil, fmt.Errorf("fail to d.typ.unmarshalWithPtr: %w", err) } - return nil, fmt.Errorf("unsupported type [%T] for toDs", i) + return dsi, nil } func (d *Decoder) toDsFromStringMap(m map[string]interface{}, nest bool, useTag bool) (*dynamicstruct.DynamicStruct, error) { @@ -172,7 +161,7 @@ func (d *Decoder) toDsFromStringMap(m map[string]interface{}, nest bool, useTag // See: https://golang.org/pkg/encoding/json/#Marshal // See: https://m-zajac.github.io/json2go/ if useTag { - tag = fmt.Sprintf(`%s:"%s"`, d.dt.string(), k) + tag = fmt.Sprintf(`%s:"%s"`, d.typ.string(), k) } // FIXME: the first character of k should be only alpha-numeric @@ -235,6 +224,9 @@ func (d *Decoder) toDsFromStringMap(m map[string]interface{}, nest bool, useTag // YAML support case nil: b = b.AddInterfaceWithTag(name, false, tag) + // HCL support + case *big.Float: + b = b.AddFloat64WithTag(name, tag) default: return nil, fmt.Errorf("unsupported type of map-value. key = [%s] value = %#v", k, value) } @@ -273,43 +265,3 @@ func (d *Decoder) addForStringMap( return b, nil } - -// Note: this is dead case with gopkg.in/yaml.v3 (but alive with v2) -// convert map[interface{}]interface{} to map[string]interface{} -// func toStringKeyMap(mapii map[interface{}]interface{}) map[string]interface{} { -// mapsi := make(map[string]interface{}) -// for k, v := range mapii { -// switch vt := v.(type) { -// case []interface{}: -// // for nest array -// mapsi[fmt.Sprintf("%v", k)] = fromArrayToMapValue(vt) -// case map[interface{}]interface{}: -// // for nest object -// mapsi[fmt.Sprintf("%v", k)] = toStringKeyMap(vt) -// default: -// mapsi[fmt.Sprintf("%v", k)] = v -// } -// } - -// return mapsi -// } - -// Note: this is dead case with gopkg.in/yaml.v3 (but alive with v2) -// func fromArrayToMapValue(ia []interface{}) interface{} { -// resIa := make([]interface{}, 0, len(ia)) -// for _, iv := range ia { -// switch ivt := iv.(type) { -// case []interface{}: -// // for nest array -// resIa = append(resIa, fromArrayToMapValue(ivt)) -// case map[interface{}]interface{}: -// // for nest object -// // !!! this is important process for map[interface{}]interface{} to map[string]interface{} for JSON unmarshaling -// resIa = append(resIa, toStringKeyMap(ivt)) -// default: -// resIa = append(resIa, ivt) -// } -// } - -// return resIa -// } diff --git a/dynamicstruct/decoder/decoder_test.go b/dynamicstruct/decoder/decoder_test.go index 6eefbfe1..78955f05 100644 --- a/dynamicstruct/decoder/decoder_test.go +++ b/dynamicstruct/decoder/decoder_test.go @@ -12,315 +12,8 @@ import ( const ( typeJSON int = iota typeYAML - typeXML -) - -var ( - singleJSON = []byte(` -{ - "null_field":null, - "string_field":"かきくけこ", - "int_field":45678, - "float32_field":9.876, - "bool_field":false, - "struct_ptr_field":{ - "key":"hugakey", - "value":"hugavalue" - }, - "array_string_field":[ - "array_str_1", - "array_str_2" - ], - "array_struct_field":[ - { - "kkk":"kkk1", - "vvvv":"vvv1" - }, - { - "kkk":"kkk2", - "vvvv":"vvv2" - }, - { - "kkk":"kkk3", - "vvvv":"vvv3" - } - ] -} -`) - - arrayJSON = []byte(` -[ - { - "null_field":null, - "string_field":"かきくけこ", - "int_field":45678, - "float32_field":9.876, - "bool_field":false, - "struct_ptr_field":{ - "key":"hugakey", - "value":"hugavalue" - }, - "array_string_field":[ - "array_str_1", - "array_str_2" - ], - "array_struct_field":[ - { - "kkk":"kkk1", - "vvvv":"vvv1" - }, - { - "kkk":"kkk2", - "vvvv":"vvv2" - }, - { - "kkk":"kkk3", - "vvvv":"vvv3" - } - ] - }, - { - "null_field":null, - "string_field":"さしすせそ", - "int_field":7890, - "float32_field":4.99, - "bool_field":true, - "struct_ptr_field":{ - "key":"hugakeyXXX", - "value":"hugavalueXXX" - }, - "array_string_field":[ - "array_str_111", - "array_str_222" - ], - "array_struct_field":[ - { - "kkk":"kkk99", - "vvvv":"vvv99" - }, - { - "kkk":"kkk999", - "vvvv":"vvv999" - }, - { - "kkk":"kkk9999", - "vvvv":"vvv9999" - } - ] - } -] -`) - - singleYAML = []byte(` -null_field: null -string_field: かきくけこ -int_field: 45678 -float32_field: 9.876 -bool_field: false -struct_ptr_field: - key: hugakey - value: hugavalue -array_string_field: - - array_str_1 - - array_str_2 -array_struct_field: - - kkk: kkk1 - vvvv: vvv1 - - kkk: kkk2 - vvvv: vvv2 - - kkk: kkk3 - vvvv: vvv3 -`) - - arrayYAML = []byte(` -- null_field: null - string_field: かきくけこ - int_field: 45678 - float32_field: 9.876 - bool_field: false - struct_ptr_field: - key: hugakey - value: hugavalue - array_string_field: - - array_str_1 - - array_str_2 - array_struct_field: - - kkk: kkk1 - vvvv: vvv1 - - kkk: kkk2 - vvvv: vvv2 - - kkk: kkk3 - vvvv: vvv3 -- null_field: null - string_field: さしすせそ - int_field: 7890 - float32_field: 4.99 - bool_field: true - struct_ptr_field: - key: hugakeyXXX - value: hugavalueXXX - array_string_field: - - array_str_111 - - array_str_222 - array_struct_field: - - kkk: kkk99 - vvvv: vvv99 - - kkk: kkk999 - vvvv: vvv999 - - kkk: kkk9999 - vvvv: vvv9999 -`) - - //lint:ignore U1000 It's ok because this is for the future. - singleTOML = []byte(` -string_field = "かきくけこ," -int_field = 45678 -float32_field = "9.876," -bool_field = false -array_string_field = ["array_str_1", "array_str_2"] - -[struct_ptr_field] - key = "hugakey" - value = "hugavalue" - -[[array_struct_field]] - kkk = "kkk1" - vvvv = "vvv1" - -[[array_struct_field]] - kkk = "kkk2" - vvvv = "vvv2" - -[[array_struct_field]] - kkk = "kkk3" - vvvv = "vvv3" -`) - - //lint:ignore U1000 It's ok because this is for the future. - singleXML = []byte(` - - - - かきくけこ - 45678 - 9.876 - false - - hugakey - hugavalue - - array_str_1 - array_str_2 - - kkk1 - vvv1 - - - kkk2 - vvv2 - - - kkk3 - vvv3 - - -`) - - //lint:ignore U1000 It's ok because this is for the future. - arrayXML = []byte(` - - - <0> - - かきくけこ - 45678 - 9.876 - false - - hugakey - hugavalue - - array_str_1 - array_str_2 - - kkk1 - vvv1 - - - kkk2 - vvv2 - - - kkk3 - vvv3 - - - <1> - - さしすせそ - 7890 - 4.99 - true - - hugakeyXXX - hugavalueXXX - - array_str_111 - array_str_222 - - kkk99 - vvv99 - - - kkk999 - vvv999 - - - kkk9999 - vvv9999 - - - -`) - - //lint:ignore U1000 It's ok because this is for the future. - singleHCL = []byte(` -"null_field" = - -"string_field" = "かきくけこ," - -"int_field" = 45678 - -"float32_field" = "9.876," - -"bool_field" = false - -"struct_ptr_field" = { - "key" = "hugakey" - - "value" = "hugavalue" -} - -"array_string_field" = ["array_str_1", "array_str_2"] - -"array_struct_field" = { - "kkk" = "kkk1" - - "vvvv" = "vvv1" -} - -"array_struct_field" = { - "kkk" = "kkk2" - - "vvvv" = "vvv2" -} - -"array_struct_field" = { - "kkk" = "kkk3" - - "vvvv" = "vvv3" -} -`) + typeHCL + typeXML // FIXME ) type decoderTest struct { @@ -347,7 +40,7 @@ func TestDynamicStructJSON(t *testing.T) { "null_field":null, "string_field":"かきくけこ", "int_field":45678, - "float32_field":9.876, + "float64_field":9.876, "bool_field":false } `), @@ -357,14 +50,14 @@ func TestDynamicStructJSON(t *testing.T) { wantNumF: 5, wantDefinition: `type DynamicStruct struct { BoolField bool - Float32Field float64 + Float64Field float64 IntField float64 NullField interface {} StringField string }`, fieldAndNestFields: map[string][]string{ "BoolField": nil, - "Float32Field": nil, + "Float64Field": nil, "IntField": nil, "NullField": nil, "StringField": nil, @@ -784,7 +477,7 @@ func TestDynamicStructYAML(t *testing.T) { null_field: null string_field: かきくけこ int_field: 45678 -float32_field: 9.876 +float64_field: 9.876 bool_field: false `), dt: typeYAML, @@ -793,14 +486,14 @@ bool_field: false wantNumF: 5, wantDefinition: `type DynamicStruct struct { BoolField bool - Float32Field float64 + Float64Field float64 IntField int NullField interface {} StringField string }`, fieldAndNestFields: map[string][]string{ "BoolField": nil, - "Float32Field": nil, + "Float64Field": nil, "IntField": nil, "NullField": nil, "StringField": nil, @@ -1078,6 +771,204 @@ string_array_field: } } +func TestDynamicStructHCL(t *testing.T) { + t.Parallel() + + tests := []decoderTest{ + { + name: "NoNestWithoutTag", + data: []byte(` +string_field = "かきくけこ" +int_field = 45678 +float64_field = 9.876 +bool_field = false +null_field = null +tuple_str_field = ["str1", "str2", "str3"] +tuple_mix_field = ["str1", 123, true] +object_str_field = { + key_a = "valA" + key_b = "valB" +} +object_mix_field = { + key_str = "strA" + key_num = 876.543 + key_bool = true +} +`), + dt: typeHCL, + nest: false, + useTag: false, + wantNumF: 9, + wantDefinition: `type DynamicStruct struct { + BoolField bool + Float64Field float64 + IntField float64 + NullField interface {} + ObjectMixField map[string]interface {} + ObjectStrField map[string]interface {} + StringField string + TupleMixField []string + TupleStrField []string +}`, + }, + { + name: "IsNestWithoutTag", + data: []byte(` +string_field = "かきくけこ" +int_field = 45678 +float64_field = 9.876 +bool_field = false +null_field = null +tuple_str_field = ["str1", "str2", "str3"] +tuple_mix_field = ["str1", 123, true] +object_str_field = { + key_a = "valA" + key_b = "valB" +} +object_mix_field = { + key_str = "strA" + key_num = 876.543 + key_bool = true +} +`), + dt: typeHCL, + nest: true, + useTag: false, + wantNumF: 9, + wantDefinition: `type DynamicStruct struct { + BoolField bool + Float64Field float64 + IntField float64 + NullField interface {} + ObjectMixField struct { + KeyBool bool + KeyNum float64 + KeyStr string + } + ObjectStrField struct { + KeyA string + KeyB string + } + StringField string + TupleMixField []string + TupleStrField []string +}`, + }, + { + name: "IsNestWithTag", + data: []byte(` +string_field = "かきくけこ" +int_field = 45678 +float64_field = 9.876 +bool_field = false +null_field = null +tuple_str_field = ["str1", "str2", "str3"] +tuple_mix_field = ["str1", 123, true] +object_str_field = { + key_a = "valA" + key_b = "valB" +} +object_mix_field = { + key_str = "strA" + key_num = 876.543 + key_bool = true +} +`), + dt: typeHCL, + nest: true, + useTag: true, + wantNumF: 9, + wantDefinition: `type DynamicStruct struct { + BoolField bool ` + "`hcl:\"bool_field\"`" + ` + Float64Field float64 ` + "`hcl:\"float64_field\"`" + ` + IntField float64 ` + "`hcl:\"int_field\"`" + ` + NullField interface {} ` + "`hcl:\"null_field\"`" + ` + ObjectMixField struct { + KeyBool bool ` + "`hcl:\"key_bool\"`" + ` + KeyNum float64 ` + "`hcl:\"key_num\"`" + ` + KeyStr string ` + "`hcl:\"key_str\"`" + ` + } ` + "`hcl:\"object_mix_field\"`" + ` + ObjectStrField struct { + KeyA string ` + "`hcl:\"key_a\"`" + ` + KeyB string ` + "`hcl:\"key_b\"`" + ` + } ` + "`hcl:\"object_str_field\"`" + ` + StringField string ` + "`hcl:\"string_field\"`" + ` + TupleMixField []string ` + "`hcl:\"tuple_mix_field\"`" + ` + TupleStrField []string ` + "`hcl:\"tuple_str_field\"`" + ` +}`, + }, + { + name: "BracketOnly", + data: []byte(`{}`), + dt: typeHCL, + nest: false, + useTag: false, + wantNumF: 0, + wantDefinition: ``, + wantErrorNew: true, + }, + { + name: "ArrayBracketOnly", + data: []byte(`[]`), + dt: typeHCL, + nest: false, + useTag: false, + wantNumF: 0, + wantDefinition: ``, + wantErrorNew: true, + }, + { + name: "OnlyLiteral", + data: []byte(`aiueo`), + dt: typeHCL, + nest: false, + useTag: false, + wantNumF: 0, + wantDefinition: ``, + wantErrorNew: true, + }, + { + name: "Empty", + data: []byte(``), + dt: typeHCL, + nest: false, + useTag: false, + wantNumF: 0, + wantDefinition: `type DynamicStruct struct { +}`, + }, + { + name: "NullData", + data: nil, + dt: typeHCL, + nest: false, + useTag: false, + wantNumF: 0, + wantDefinition: `type DynamicStruct struct { +}`, + }, + } + + for _, tt := range tests { + tt := tt // See: https://gist.github.com/posener/92a55c4cd441fc5e5e85f27bca008721 + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + dec, err := FromHCL(tt.data) + if err != nil { + if !tt.wantErrorNew { + t.Fatalf("unexpected error is returned from FromHCL: %v", err) + } + return + } else if tt.wantErrorNew { + t.Fatalf("error is expected but it does not occur from FromHCL. data: %q", string(tt.data)) + } + + testCorrectCase(t, tt, dec) + }) + } +} + func TestDynamicStructFixmeXml(t *testing.T) { t.Parallel() @@ -1088,7 +979,7 @@ func TestDynamicStructFixmeXml(t *testing.T) { null_field: null string_field: かきくけこ int_field: 45678 -float32_field: 9.876 +float64_field: 9.876 bool_field: false `), dt: typeXML, @@ -1137,10 +1028,6 @@ bool_field: false func testCorrectCase(t *testing.T, tt decoderTest, dec *Decoder) { t.Helper() - if d := cmp.Diff(dec.OrgData(), tt.data); d != "" { - t.Fatalf("mismatch OrgData: (-got +want)\n%s", d) - } - ds, err := dec.DynamicStruct(tt.nest, tt.useTag) if err != nil { if !tt.wantErrorDs { diff --git a/getter.go b/getter.go index ba164fae..ac277a94 100644 --- a/getter.go +++ b/getter.go @@ -94,27 +94,27 @@ func (g *Getter) Names() []string { return g.names } -func (g *Getter) getSafely(name string) (*getterField, bool) { +func (g *Getter) getField(name string) (*getterField, bool) { gf, ok := g.fields[name] return gf, ok } // goroutine-safely and kind-safely access to a getterField by name -func (g *Getter) getSafelyKindly(name string, kind reflect.Kind) (*getterField, bool) { - gf, ok := g.getSafely(name) +func (g *Getter) getFieldKindly(name string, kind reflect.Kind) (*getterField, bool) { + gf, ok := g.getField(name) return gf, ok && gf.isKind(kind) } // Has tests whether the original struct has a field named "name". func (g *Getter) Has(name string) bool { - _, ok := g.getSafely(name) + _, ok := g.getField(name) return ok } // GetType returns the reflect.Type object of the original struct field named "name". // 2nd return value will be false if the original struct does not have a "name" field. func (g *Getter) GetType(name string) (reflect.Type, bool) { - gf, ok := g.getSafely(name) + gf, ok := g.getField(name) if ok { return gf.typ, true } @@ -125,7 +125,7 @@ func (g *Getter) GetType(name string) (reflect.Type, bool) { // GetValue returns the reflect.Value object of the original struct field named "name". // 2nd return value will be false if the original struct does not have a "name" field. func (g *Getter) GetValue(name string) (reflect.Value, bool) { - gf, ok := g.getSafely(name) + gf, ok := g.getField(name) if ok { return gf.indirect, true } @@ -136,7 +136,7 @@ func (g *Getter) GetValue(name string) (reflect.Value, bool) { // Get returns the interface of the original struct field named name. // 2nd return value will be false if the original struct does not have a "name" field. func (g *Getter) Get(name string) (interface{}, bool) { - gf, ok := g.getSafely(name) + gf, ok := g.getField(name) if ok { return gf.intf, true } @@ -156,7 +156,7 @@ func (g *Getter) ToMap() map[string]interface{} { // IsSlice reports whether type of the original struct field named name is slice. func (g *Getter) IsSlice(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Slice) + _, ok := g.getFieldKindly(name, reflect.Slice) return ok } @@ -164,7 +164,7 @@ func (g *Getter) IsSlice(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not slice of interface. func (g *Getter) Slice(name string) ([]interface{}, bool) { - gf, ok := g.getSafelyKindly(name, reflect.Slice) + gf, ok := g.getFieldKindly(name, reflect.Slice) if !ok { return nil, false } @@ -181,7 +181,7 @@ func (g *Getter) Slice(name string) ([]interface{}, bool) { // IsBool reports whether type of the original struct field named name is bool. func (g *Getter) IsBool(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Bool) + _, ok := g.getFieldKindly(name, reflect.Bool) return ok } @@ -189,7 +189,7 @@ func (g *Getter) IsBool(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not bool. func (g *Getter) Bool(name string) (bool, bool) { - gf, ok := g.getSafelyKindly(name, reflect.Bool) + gf, ok := g.getFieldKindly(name, reflect.Bool) if !ok { return false, false } @@ -200,7 +200,7 @@ func (g *Getter) Bool(name string) (bool, bool) { // IsByte reports whether type of the original struct field named name is byte. func (g *Getter) IsByte(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Uint8) + _, ok := g.getFieldKindly(name, reflect.Uint8) return ok } @@ -208,7 +208,7 @@ func (g *Getter) IsByte(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not byte. func (g *Getter) Byte(name string) (byte, bool) { - gf, ok := g.getSafelyKindly(name, reflect.Uint8) + gf, ok := g.getFieldKindly(name, reflect.Uint8) if !ok { return 0, false } @@ -219,7 +219,7 @@ func (g *Getter) Byte(name string) (byte, bool) { // IsBytes reports whether type of the original struct field named name is []byte. func (g *Getter) IsBytes(name string) bool { - gf, ok := g.getSafelyKindly(name, reflect.Slice) + gf, ok := g.getFieldKindly(name, reflect.Slice) if !ok { return false } @@ -231,7 +231,7 @@ func (g *Getter) IsBytes(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not []byte. func (g *Getter) Bytes(name string) ([]byte, bool) { - gf, ok := g.getSafelyKindly(name, reflect.Slice) + gf, ok := g.getFieldKindly(name, reflect.Slice) if !ok { return nil, false } @@ -242,7 +242,7 @@ func (g *Getter) Bytes(name string) ([]byte, bool) { // IsString reports whether type of the original struct field named name is string. func (g *Getter) IsString(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.String) + _, ok := g.getFieldKindly(name, reflect.String) return ok } @@ -250,7 +250,7 @@ func (g *Getter) IsString(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not string. func (g *Getter) String(name string) (string, bool) { - gf, ok := g.getSafelyKindly(name, reflect.String) + gf, ok := g.getFieldKindly(name, reflect.String) if !ok { return "", false } @@ -261,7 +261,7 @@ func (g *Getter) String(name string) (string, bool) { // IsInt reports whether type of the original struct field named name is int. func (g *Getter) IsInt(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Int) + _, ok := g.getFieldKindly(name, reflect.Int) return ok } @@ -269,7 +269,7 @@ func (g *Getter) IsInt(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not int. func (g *Getter) Int(name string) (int, bool) { - gf, ok := g.getSafelyKindly(name, reflect.Int) + gf, ok := g.getFieldKindly(name, reflect.Int) if !ok { return 0, false } @@ -280,7 +280,7 @@ func (g *Getter) Int(name string) (int, bool) { // IsInt8 reports whether type of the original struct field named name is int8. func (g *Getter) IsInt8(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Int8) + _, ok := g.getFieldKindly(name, reflect.Int8) return ok } @@ -288,7 +288,7 @@ func (g *Getter) IsInt8(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not int8. func (g *Getter) Int8(name string) (int8, bool) { - gf, ok := g.getSafelyKindly(name, reflect.Int8) + gf, ok := g.getFieldKindly(name, reflect.Int8) if !ok { return 0, false } @@ -299,7 +299,7 @@ func (g *Getter) Int8(name string) (int8, bool) { // IsInt16 reports whether type of the original struct field named name is int16. func (g *Getter) IsInt16(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Int16) + _, ok := g.getFieldKindly(name, reflect.Int16) return ok } @@ -307,7 +307,7 @@ func (g *Getter) IsInt16(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not int16. func (g *Getter) Int16(name string) (int16, bool) { - gf, ok := g.getSafelyKindly(name, reflect.Int16) + gf, ok := g.getFieldKindly(name, reflect.Int16) if !ok { return 0, false } @@ -318,7 +318,7 @@ func (g *Getter) Int16(name string) (int16, bool) { // IsInt32 reports whether type of the original struct field named name is int32. func (g *Getter) IsInt32(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Int32) + _, ok := g.getFieldKindly(name, reflect.Int32) return ok } @@ -326,7 +326,7 @@ func (g *Getter) IsInt32(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not int32. func (g *Getter) Int32(name string) (int32, bool) { - gf, ok := g.getSafelyKindly(name, reflect.Int32) + gf, ok := g.getFieldKindly(name, reflect.Int32) if !ok { return 0, false } @@ -337,7 +337,7 @@ func (g *Getter) Int32(name string) (int32, bool) { // IsInt64 reports whether type of the original struct field named name is int64. func (g *Getter) IsInt64(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Int64) + _, ok := g.getFieldKindly(name, reflect.Int64) return ok } @@ -345,7 +345,7 @@ func (g *Getter) IsInt64(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not int64. func (g *Getter) Int64(name string) (int64, bool) { - gf, ok := g.getSafelyKindly(name, reflect.Int64) + gf, ok := g.getFieldKindly(name, reflect.Int64) if !ok { return 0, false } @@ -356,7 +356,7 @@ func (g *Getter) Int64(name string) (int64, bool) { // IsUint reports whether type of the original struct field named name is uint. func (g *Getter) IsUint(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Uint) + _, ok := g.getFieldKindly(name, reflect.Uint) return ok } @@ -364,7 +364,7 @@ func (g *Getter) IsUint(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not uint. func (g *Getter) Uint(name string) (uint, bool) { - gf, ok := g.getSafelyKindly(name, reflect.Uint) + gf, ok := g.getFieldKindly(name, reflect.Uint) if !ok { return 0, false } @@ -375,7 +375,7 @@ func (g *Getter) Uint(name string) (uint, bool) { // IsUint8 reports whether type of the original struct field named name is uint8. func (g *Getter) IsUint8(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Uint8) + _, ok := g.getFieldKindly(name, reflect.Uint8) return ok } @@ -383,7 +383,7 @@ func (g *Getter) IsUint8(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not uint8. func (g *Getter) Uint8(name string) (uint8, bool) { - gf, ok := g.getSafelyKindly(name, reflect.Uint8) + gf, ok := g.getFieldKindly(name, reflect.Uint8) if !ok { return 0, false } @@ -394,7 +394,7 @@ func (g *Getter) Uint8(name string) (uint8, bool) { // IsUint16 reports whether type of the original struct field named name is uint16. func (g *Getter) IsUint16(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Uint16) + _, ok := g.getFieldKindly(name, reflect.Uint16) return ok } @@ -402,7 +402,7 @@ func (g *Getter) IsUint16(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not uint16. func (g *Getter) Uint16(name string) (uint16, bool) { - gf, ok := g.getSafelyKindly(name, reflect.Uint16) + gf, ok := g.getFieldKindly(name, reflect.Uint16) if !ok { return 0, false } @@ -413,7 +413,7 @@ func (g *Getter) Uint16(name string) (uint16, bool) { // IsUint32 reports whether type of the original struct field named name is uint32. func (g *Getter) IsUint32(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Uint32) + _, ok := g.getFieldKindly(name, reflect.Uint32) return ok } @@ -421,7 +421,7 @@ func (g *Getter) IsUint32(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not uint32. func (g *Getter) Uint32(name string) (uint32, bool) { - gf, ok := g.getSafelyKindly(name, reflect.Uint32) + gf, ok := g.getFieldKindly(name, reflect.Uint32) if !ok { return 0, false } @@ -432,7 +432,7 @@ func (g *Getter) Uint32(name string) (uint32, bool) { // IsUint64 reports whether type of the original struct field named name is uint64. func (g *Getter) IsUint64(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Uint64) + _, ok := g.getFieldKindly(name, reflect.Uint64) return ok } @@ -440,7 +440,7 @@ func (g *Getter) IsUint64(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not uint64. func (g *Getter) Uint64(name string) (uint64, bool) { - gf, ok := g.getSafelyKindly(name, reflect.Uint64) + gf, ok := g.getFieldKindly(name, reflect.Uint64) if !ok { return 0, false } @@ -451,7 +451,7 @@ func (g *Getter) Uint64(name string) (uint64, bool) { // IsUintptr reports whether type of the original struct field named name is uintptr. func (g *Getter) IsUintptr(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Uintptr) + _, ok := g.getFieldKindly(name, reflect.Uintptr) return ok } @@ -459,7 +459,7 @@ func (g *Getter) IsUintptr(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not uintptr. func (g *Getter) Uintptr(name string) (uintptr, bool) { - gf, ok := g.getSafelyKindly(name, reflect.Uintptr) + gf, ok := g.getFieldKindly(name, reflect.Uintptr) if !ok { return 0, false } @@ -470,7 +470,7 @@ func (g *Getter) Uintptr(name string) (uintptr, bool) { // IsFloat32 reports whether type of the original struct field named name is float32. func (g *Getter) IsFloat32(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Float32) + _, ok := g.getFieldKindly(name, reflect.Float32) return ok } @@ -478,7 +478,7 @@ func (g *Getter) IsFloat32(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not float32. func (g *Getter) Float32(name string) (float32, bool) { - gf, ok := g.getSafelyKindly(name, reflect.Float32) + gf, ok := g.getFieldKindly(name, reflect.Float32) if !ok { return 0, false } @@ -489,7 +489,7 @@ func (g *Getter) Float32(name string) (float32, bool) { // IsFloat64 reports whether type of the original struct field named name is float64. func (g *Getter) IsFloat64(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Float64) + _, ok := g.getFieldKindly(name, reflect.Float64) return ok } @@ -497,7 +497,7 @@ func (g *Getter) IsFloat64(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not float64. func (g *Getter) Float64(name string) (float64, bool) { - gf, ok := g.getSafelyKindly(name, reflect.Float64) + gf, ok := g.getFieldKindly(name, reflect.Float64) if !ok { return 0, false } @@ -508,7 +508,7 @@ func (g *Getter) Float64(name string) (float64, bool) { // IsComplex64 reports whether type of the original struct field named name is []byte. func (g *Getter) IsComplex64(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Complex64) + _, ok := g.getFieldKindly(name, reflect.Complex64) return ok } @@ -516,7 +516,7 @@ func (g *Getter) IsComplex64(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not complex64. func (g *Getter) Complex64(name string) (complex64, bool) { - gf, ok := g.getSafelyKindly(name, reflect.Complex64) + gf, ok := g.getFieldKindly(name, reflect.Complex64) if !ok { return 0, false } @@ -527,7 +527,7 @@ func (g *Getter) Complex64(name string) (complex64, bool) { // IsComplex128 reports whether type of the original struct field named name is []byte. func (g *Getter) IsComplex128(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Complex128) + _, ok := g.getFieldKindly(name, reflect.Complex128) return ok } @@ -535,7 +535,7 @@ func (g *Getter) IsComplex128(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not complex128. func (g *Getter) Complex128(name string) (complex128, bool) { - gf, ok := g.getSafelyKindly(name, reflect.Complex128) + gf, ok := g.getFieldKindly(name, reflect.Complex128) if !ok { return 0, false } @@ -546,7 +546,7 @@ func (g *Getter) Complex128(name string) (complex128, bool) { // IsUnsafePointer reports whether type of the original struct field named name is []byte. func (g *Getter) IsUnsafePointer(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.UnsafePointer) + _, ok := g.getFieldKindly(name, reflect.UnsafePointer) return ok } @@ -554,7 +554,7 @@ func (g *Getter) IsUnsafePointer(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not unsafe.Pointer. func (g *Getter) UnsafePointer(name string) (unsafe.Pointer, bool) { - gf, ok := g.getSafelyKindly(name, reflect.UnsafePointer) + gf, ok := g.getFieldKindly(name, reflect.UnsafePointer) if !ok { return nil, false } @@ -565,31 +565,31 @@ func (g *Getter) UnsafePointer(name string) (unsafe.Pointer, bool) { // IsMap reports whether type of the original struct field named name is map. func (g *Getter) IsMap(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Map) + _, ok := g.getFieldKindly(name, reflect.Map) return ok } // IsFunc reports whether type of the original struct field named name is func. func (g *Getter) IsFunc(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Func) + _, ok := g.getFieldKindly(name, reflect.Func) return ok } // IsChan reports whether type of the original struct field named name is chan. func (g *Getter) IsChan(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Chan) + _, ok := g.getFieldKindly(name, reflect.Chan) return ok } // IsStruct reports whether type of the original struct field named name is struct. func (g *Getter) IsStruct(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Struct) + _, ok := g.getFieldKindly(name, reflect.Struct) return ok } // IsArray reports whether type of the original struct field named name is slice. func (g *Getter) IsArray(name string) bool { - _, ok := g.getSafelyKindly(name, reflect.Array) + _, ok := g.getFieldKindly(name, reflect.Array) return ok } @@ -597,7 +597,7 @@ func (g *Getter) IsArray(name string) bool { // 2nd return value will be false if the original struct does not have a "name" field. // 2nd return value will be false if type of the original struct "name" field is not struct or struct pointer. func (g *Getter) GetGetter(name string) (*Getter, bool) { - gf, ok := g.getSafelyKindly(name, reflect.Struct) + gf, ok := g.getFieldKindly(name, reflect.Struct) if !ok { return nil, false } @@ -608,7 +608,7 @@ func (g *Getter) GetGetter(name string) (*Getter, bool) { // MapGet returns the interface slice of mapped values of the original struct field named name. func (g *Getter) MapGet(name string, f func(int, *Getter) (interface{}, error)) ([]interface{}, error) { - gf, ok := g.getSafelyKindly(name, reflect.Slice) + gf, ok := g.getFieldKindly(name, reflect.Slice) if !ok { return nil, fmt.Errorf("field %s does not exist or is not slice type", name) } diff --git a/go.mod b/go.mod index cdd8982a..96bec9bf 100644 --- a/go.mod +++ b/go.mod @@ -4,15 +4,20 @@ go 1.19 require ( github.com/google/go-cmp v0.5.8 + github.com/hashicorp/hcl/v2 v2.13.0 github.com/iancoleman/strcase v0.2.0 github.com/spf13/viper v1.12.0 + github.com/zclconf/go-cty v1.10.0 gopkg.in/yaml.v3 v3.0.1 ) require ( + github.com/agext/levenshtein v1.2.3 // indirect + github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/magiconair/properties v1.8.6 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.2 // indirect diff --git a/go.sum b/go.sum index 14890866..a817de12 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,10 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= +github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= +github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= +github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= @@ -61,6 +65,7 @@ github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmV github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -121,6 +126,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/hcl/v2 v2.13.0 h1:0Apadu1w6M11dyGFxWnmhhcMjkbAiKCv7G1r/2QgCNc= +github.com/hashicorp/hcl/v2 v2.13.0/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0= github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -134,8 +141,11 @@ github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= @@ -149,6 +159,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= @@ -168,10 +179,14 @@ github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8 github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/subosito/gotenv v1.4.0 h1:yAzM1+SmVcz5R4tXGsNMu1jUl2aOJXoiWUCEwwnGrvs= github.com/subosito/gotenv v1.4.0/go.mod h1:mZd6rFysKEcUhUHXJk0C/08wAgyDBFuwEYL7vWWGaGo= +github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= +github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zclconf/go-cty v1.10.0 h1:mp9ZXQeIcN8kAwuqorjH+Q+njbJKjLrvB2yIh4q7U+0= +github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -312,6 +327,7 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/version.go b/version.go index a82109a7..cac46c43 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package structil // VERSION says my version number -const VERSION = "0.9.1" +const VERSION = "0.10.0" diff --git a/version_test.go b/version_test.go index 9e3b287f..3dbfb1ae 100644 --- a/version_test.go +++ b/version_test.go @@ -7,7 +7,7 @@ import ( ) func TestVersion(t *testing.T) { - exp := "0.9.1" + exp := "0.10.0" if VERSION != exp { t.Errorf("expected: %#v, but actual: %#v", exp, VERSION)