mirror of
https://github.com/gohugoio/hugo.git
synced 2026-08-24 15:28:54 +00:00
Fix it so YAML integer types can be used where Go int types are expected
E.g. in date.AddDate. In Hugo v0.152.0 we moved to a new YAML library (github.com/goccy/go-yaml) which produces uint64 for unsigned integers. This unfortunately breaks common constructs like: .Date.AddDate 0 0 7 when .Date is a time.Time and the integers are unmarshaled from YAML front matter. This commit adds code to handle conversion from uint64 (and other int types) to the required int types where possible. Fixes #14079
This commit is contained in:
@@ -18,6 +18,7 @@ package hreflect
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -309,3 +310,29 @@ func IsContextType(tp reflect.Type) bool {
|
||||
})
|
||||
return isContext
|
||||
}
|
||||
|
||||
// ConvertIfPossible tries to convert val to typ if possible.
|
||||
// This is currently only implemented for int kinds,
|
||||
// added to handle the move to a new YAML library which produces uint64 for unsigned integers.
|
||||
// We can expand on this later if needed.
|
||||
// See Issue 14079.
|
||||
func ConvertIfPossible(val reflect.Value, typ reflect.Type) (reflect.Value, bool) {
|
||||
if IsInt(typ.Kind()) {
|
||||
if IsInt(val.Kind()) {
|
||||
if typ.OverflowInt(val.Int()) {
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
return val.Convert(typ), true
|
||||
}
|
||||
if IsUint(val.Kind()) {
|
||||
if val.Uint() > uint64(math.MaxInt64) {
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
if typ.OverflowInt(int64(val.Uint())) {
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
return val.Convert(typ), true
|
||||
}
|
||||
}
|
||||
return reflect.Value{}, false
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ package hreflect
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -176,3 +177,66 @@ func BenchmarkGetMethodByNamePara(b *testing.B) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCastIfPossible(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
value any
|
||||
typ any
|
||||
expected any
|
||||
ok bool
|
||||
}{
|
||||
// From uint to int.
|
||||
{
|
||||
name: "uint64(math.MaxUint64) to int16",
|
||||
value: uint64(math.MaxUint64),
|
||||
typ: int16(0),
|
||||
ok: false, // overflow
|
||||
},
|
||||
|
||||
{
|
||||
name: "uint64(math.MaxUint64) to int64",
|
||||
value: uint64(math.MaxUint64),
|
||||
typ: int64(0),
|
||||
ok: false, // overflow
|
||||
},
|
||||
{
|
||||
name: "uint64(math.MaxInt16) to int16",
|
||||
value: uint64(math.MaxInt16),
|
||||
typ: int64(0),
|
||||
ok: true,
|
||||
expected: int64(math.MaxInt16),
|
||||
},
|
||||
// From int to int.
|
||||
{
|
||||
name: "int64(math.MaxInt64) to int16",
|
||||
value: int64(math.MaxInt64),
|
||||
typ: int16(0),
|
||||
ok: false, // overflow
|
||||
},
|
||||
{
|
||||
name: "int64(math.MaxInt16) to int",
|
||||
value: int64(math.MaxInt16),
|
||||
typ: int(0),
|
||||
ok: true,
|
||||
expected: int(math.MaxInt16),
|
||||
},
|
||||
|
||||
{
|
||||
name: "int64(math.MaxInt16) to int",
|
||||
value: int64(math.MaxInt16),
|
||||
typ: int(0),
|
||||
ok: true,
|
||||
expected: int(math.MaxInt16),
|
||||
},
|
||||
} {
|
||||
|
||||
v, ok := ConvertIfPossible(reflect.ValueOf(test.value), reflect.TypeOf(test.typ))
|
||||
c.Assert(ok, qt.Equals, test.ok, qt.Commentf("test case: %s", test.name))
|
||||
if test.ok {
|
||||
c.Assert(v.Interface(), qt.Equals, test.expected, qt.Commentf("test case: %s", test.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user