From e1eea914529911db509e58fa75a7769630e5d83c Mon Sep 17 00:00:00 2001 From: Kenneth Shaw Date: Tue, 12 Nov 2024 07:09:03 +0700 Subject: [PATCH] Wrap reflect.Type.ConvertibleTo calls with Go/TinyGo implementations Enable package to be usable with [TinyGo](https://tinygo.org). Changes calls to reflect.Type.ConvertibleTo in decode.go to Go/TinyGo specific implementations. Basic examples work, but (some) unit tests fail as conversions to `interface{}` do not currently work in TinyGo. Otherwise, all other functionality I've tested now appears to work properly: highlighted error output, `cmd/ycat`, the README.md examples, etc. As an example, both Go and TinyGo are now able to use this package vis-a-vis this example taken from README.md: ```go package main import ( "fmt" "strings" "github.com/goccy/go-yaml" ) func main() { yml := ` store: book: - author: john price: 10 - author: ken price: 12 bicycle: color: red price: 19.95 ` path, err := yaml.PathString("$.store.book[*].author") if err != nil { //... } var authors []string if err := path.Read(strings.NewReader(yml), &authors); err != nil { //... } fmt.Println(authors) } ``` Output: ```sh ken@ken-desktop:~/g$ go run main.go [john ken] ken@ken-desktop:~/g$ tinygo run main.go [john ken] ken@ken-desktop:~/g$ ``` (previously the `tinygo run` would panic) --- conv_go.go | 11 +++++++++ conv_tinygo.go | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++ decode.go | 10 ++++---- 3 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 conv_go.go create mode 100644 conv_tinygo.go diff --git a/conv_go.go b/conv_go.go new file mode 100644 index 00000000..9f578c39 --- /dev/null +++ b/conv_go.go @@ -0,0 +1,11 @@ +//go:build !tinygo + +package yaml + +import ( + "reflect" +) + +func convertibleTo(src reflect.Value, typ reflect.Type) bool { + return src.Type().ConvertibleTo(typ) +} diff --git a/conv_tinygo.go b/conv_tinygo.go new file mode 100644 index 00000000..c26ffcde --- /dev/null +++ b/conv_tinygo.go @@ -0,0 +1,65 @@ +//go:build tinygo + +package yaml + +import ( + "reflect" +) + +func convertibleTo(src reflect.Value, typ reflect.Type) bool { + srck, typk := src.Kind(), typ.Kind() + if srck == typk { + return true + } + switch srck { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch typk { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return true + case reflect.Float32, reflect.Float64: + return true + case reflect.String: + return true + } + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + switch typk { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return true + case reflect.Float32, reflect.Float64: + return true + case reflect.String: + return true + } + + case reflect.Float32, reflect.Float64: + switch typk { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return true + case reflect.Float32, reflect.Float64: + return true + } + + case reflect.Slice: + if typk == reflect.String /*&& !src.Type().Elem().isNamed()*/ { + switch src.Type().Elem().Kind() { + case reflect.Uint8, reflect.Int32: + return true + } + } + + case reflect.String: + switch typk { + case reflect.Slice: + switch typ.Elem().Kind() { + case reflect.Uint8, reflect.Int32: + return true + } + return false + } + } + + return false +} diff --git a/decode.go b/decode.go index fe990e54..0cad76b8 100644 --- a/decode.go +++ b/decode.go @@ -582,7 +582,7 @@ func (d *Decoder) getArrayNode(node ast.Node) (ast.ArrayNode, error) { func (d *Decoder) convertValue(v reflect.Value, typ reflect.Type, src ast.Node) (reflect.Value, error) { if typ.Kind() != reflect.String { - if !v.Type().ConvertibleTo(typ) { + if !convertibleTo(v, typ) { // Special case for "strings -> floats" aka scientific notation // If the destination type is a float and the source type is a string, check if we can @@ -613,7 +613,7 @@ func (d *Decoder) convertValue(v reflect.Value, typ reflect.Type, src ast.Node) case reflect.Bool: return reflect.ValueOf(fmt.Sprint(v.Bool())), nil } - if !v.Type().ConvertibleTo(typ) { + if !convertibleTo(v, typ) { return reflect.Zero(typ), errors.ErrTypeMismatch(typ, v.Type(), src.GetToken()) } return v.Convert(typ), nil @@ -868,9 +868,7 @@ func (d *Decoder) decodeByUnmarshaler(ctx context.Context, dst reflect.Value, sr return fmt.Errorf("does not implemented Unmarshaler") } -var ( - astNodeType = reflect.TypeOf((*ast.Node)(nil)).Elem() -) +var astNodeType = reflect.TypeOf((*ast.Node)(nil)).Elem() func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.Node) error { if src.Type() == ast.AnchorType { @@ -1634,7 +1632,7 @@ func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node return err } k = reflect.ValueOf(keyVal) - if k.IsValid() && k.Type().ConvertibleTo(keyType) { + if k.IsValid() && convertibleTo(k, keyType) { k = k.Convert(keyType) } }