Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

better detection of pointer receivers #979

Open
wants to merge 2 commits into
base: v3
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 36 additions & 23 deletions encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"sort"
"strconv"
"strings"
"time"
"unicode/utf8"
)

Expand Down Expand Up @@ -110,31 +109,34 @@ func (e *encoder) marshalDoc(tag string, in reflect.Value) {

func (e *encoder) marshal(tag string, in reflect.Value) {
tag = shortTag(tag)
if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() {
if !in.IsValid() { //|| in.Kind() == reflect.Ptr && in.IsNil() {
e.nilv()
return
}

iface := in.Interface()
switch value := iface.(type) {
case *Node:
e.nodev(in)
if iface == nil {
e.nilv()
return
case Node:
if !in.CanAddr() {
var n = reflect.New(in.Type()).Elem()
}

if in.Kind() != reflect.Ptr && in.Kind() != reflect.Interface {
if in.CanAddr() {
iface = in.Addr().Interface()
} else {
// uh, not sure about this but look at this project's original Node: case...
n := reflect.New(in.Type()).Elem()
n.Set(in)
in = n
iface = n.Addr().Interface()
}
e.nodev(in.Addr())
return
case time.Time:
e.timev(tag, in)
return
case *time.Time:
e.timev(tag, in.Elem())
return
case time.Duration:
e.stringv(tag, reflect.ValueOf(value.String()))
}

switch value := iface.(type) {
//case *time.Duration:
// e.stringv(tag, reflect.ValueOf(value.String()))
// return
case *Node:
e.nodev(in)
return
case Marshaler:
v, err := value.MarshalYAML()
Expand All @@ -143,20 +145,25 @@ func (e *encoder) marshal(tag string, in reflect.Value) {
}
if v == nil {
e.nilv()
return
} else {
e.marshal(tag, reflect.ValueOf(v))
}
e.marshal(tag, reflect.ValueOf(v))
return
case encoding.TextMarshaler:
text, err := value.MarshalText()
if err != nil {
fail(err)
}
in = reflect.ValueOf(string(text))
e.rawstringv(tag, string(text))
return
case fmt.Stringer:
e.rawstringv(tag, value.String())
return
case nil:
e.nilv()
return
}

switch in.Kind() {
case reflect.Interface:
e.marshal(tag, in.Elem())
Expand Down Expand Up @@ -322,8 +329,12 @@ func isOldBool(s string) (result bool) {
}

func (e *encoder) stringv(tag string, in reflect.Value) {
var style yaml_scalar_style_t
s := in.String()
e.rawstringv(tag, s)
}

func (e *encoder) rawstringv(tag string, s string) {
var style yaml_scalar_style_t
canUsePlain := true
switch {
case !utf8.ValidString(s):
Expand Down Expand Up @@ -382,11 +393,13 @@ func (e *encoder) uintv(tag string, in reflect.Value) {
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
}

/* Handled by TextMarshaler in time.Time
func (e *encoder) timev(tag string, in reflect.Value) {
t := in.Interface().(time.Time)
s := t.Format(time.RFC3339Nano)
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
}
*/

func (e *encoder) floatv(tag string, in reflect.Value) {
// Issue #352: When formatting, use the precision of the underlying value
Expand Down
92 changes: 51 additions & 41 deletions yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
//
// Source code and other details for the project are available at GitHub:
//
// https://github.com/go-yaml/yaml
//
// https://github.com/go-yaml/yaml
package yaml

import (
Expand Down Expand Up @@ -75,16 +74,15 @@ type Marshaler interface {
//
// For example:
//
// type T struct {
// F int `yaml:"a,omitempty"`
// B int
// }
// var t T
// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
// type T struct {
// F int `yaml:"a,omitempty"`
// B int
// }
// var t T
// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
//
// See the documentation of Marshal for the format of tags and a list of
// supported tag options.
//
func Unmarshal(in []byte, out interface{}) (err error) {
return unmarshal(in, out, false)
}
Expand Down Expand Up @@ -185,36 +183,35 @@ func unmarshal(in []byte, out interface{}, strict bool) (err error) {
//
// The field tag format accepted is:
//
// `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
// `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
//
// The following flags are currently supported:
//
// omitempty Only include the field if it's not set to the zero
// value for the type or to empty slices or maps.
// Zero valued structs will be omitted if all their public
// fields are zero, unless they implement an IsZero
// method (see the IsZeroer interface type), in which
// case the field will be excluded if IsZero returns true.
// omitempty Only include the field if it's not set to the zero
// value for the type or to empty slices or maps.
// Zero valued structs will be omitted if all their public
// fields are zero, unless they implement an IsZero
// method (see the IsZeroer interface type), in which
// case the field will be excluded if IsZero returns true.
//
// flow Marshal using a flow style (useful for structs,
// sequences and maps).
// flow Marshal using a flow style (useful for structs,
// sequences and maps).
//
// inline Inline the field, which must be a struct or a map,
// causing all of its fields or keys to be processed as if
// they were part of the outer struct. For maps, keys must
// not conflict with the yaml keys of other struct fields.
// inline Inline the field, which must be a struct or a map,
// causing all of its fields or keys to be processed as if
// they were part of the outer struct. For maps, keys must
// not conflict with the yaml keys of other struct fields.
//
// In addition, if the key is "-", the field is ignored.
//
// For example:
//
// type T struct {
// F int `yaml:"a,omitempty"`
// B int
// }
// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"
//
// type T struct {
// F int `yaml:"a,omitempty"`
// B int
// }
// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"
func Marshal(in interface{}) (out []byte, err error) {
defer handleErr(&err)
e := newEncoder()
Expand Down Expand Up @@ -358,22 +355,21 @@ const (
//
// For example:
//
// var person struct {
// Name string
// Address yaml.Node
// }
// err := yaml.Unmarshal(data, &person)
//
// Or by itself:
// var person struct {
// Name string
// Address yaml.Node
// }
// err := yaml.Unmarshal(data, &person)
//
// var person Node
// err := yaml.Unmarshal(data, &person)
// Or by itself:
//
// var person Node
// err := yaml.Unmarshal(data, &person)
type Node struct {
// Kind defines whether the node is a document, a mapping, a sequence,
// a scalar value, or an alias to another node. The specific data type of
// scalar nodes may be obtained via the ShortTag and LongTag methods.
Kind Kind
Kind Kind

// Style allows customizing the apperance of the node in the tree.
Style Style
Expand Down Expand Up @@ -421,7 +417,6 @@ func (n *Node) IsZero() bool {
n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0
}


// LongTag returns the long form of the tag that indicates the data type for
// the node. If the Tag field isn't explicitly defined, one will be computed
// based on the node properties.
Expand Down Expand Up @@ -659,7 +654,22 @@ type IsZeroer interface {

func isZero(v reflect.Value) bool {
kind := v.Kind()
if z, ok := v.Interface().(IsZeroer); ok {

var iface any
if kind != reflect.Ptr && kind != reflect.Interface {
if v.CanAddr() {
iface = v.Addr().Interface()
} else {
// uh, not sure about this but look at this project's original Node: case...
n := reflect.New(v.Type()).Elem()
n.Set(v)
iface = n.Addr().Interface()
}
} else {
iface = v.Interface()
}

if z, ok := iface.(IsZeroer); ok {
if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() {
return true
}
Expand Down