Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd4c7888

Browse files
committed
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 7when .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
1 parent29e2c2f commitd4c7888

File tree

5 files changed

+161
-1
lines changed

5 files changed

+161
-1
lines changed

‎common/hreflect/helpers.go‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package hreflect
1818

1919
import (
2020
"context"
21+
"math"
2122
"reflect"
2223
"sync"
2324
"time"
@@ -309,3 +310,29 @@ func IsContextType(tp reflect.Type) bool {
309310
})
310311
returnisContext
311312
}
313+
314+
// ConvertIfPossible tries to convert val to typ if possible.
315+
// This is currently only implemented for int kinds,
316+
// added to handle the move to a new YAML library which produces uint64 for unsigned integers.
317+
// We can expand on this later if needed.
318+
// See Issue 14079.
319+
funcConvertIfPossible(val reflect.Value,typ reflect.Type) (reflect.Value,bool) {
320+
ifIsInt(typ.Kind()) {
321+
ifIsInt(val.Kind()) {
322+
iftyp.OverflowInt(val.Int()) {
323+
return reflect.Value{},false
324+
}
325+
returnval.Convert(typ),true
326+
}
327+
ifIsUint(val.Kind()) {
328+
ifval.Uint()>uint64(math.MaxInt64) {
329+
return reflect.Value{},false
330+
}
331+
iftyp.OverflowInt(int64(val.Uint())) {
332+
return reflect.Value{},false
333+
}
334+
returnval.Convert(typ),true
335+
}
336+
}
337+
return reflect.Value{},false
338+
}

‎common/hreflect/helpers_test.go‎

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ package hreflect
1515

1616
import (
1717
"context"
18+
"math"
1819
"reflect"
1920
"testing"
2021
"time"
@@ -176,3 +177,66 @@ func BenchmarkGetMethodByNamePara(b *testing.B) {
176177
}
177178
})
178179
}
180+
181+
funcTestCastIfPossible(t*testing.T) {
182+
c:=qt.New(t)
183+
184+
for_,test:=range []struct {
185+
namestring
186+
valueany
187+
typany
188+
expectedany
189+
okbool
190+
}{
191+
// From uint to int.
192+
{
193+
name:"uint64(math.MaxUint64) to int16",
194+
value:uint64(math.MaxUint64),
195+
typ:int16(0),
196+
ok:false,// overflow
197+
},
198+
199+
{
200+
name:"uint64(math.MaxUint64) to int64",
201+
value:uint64(math.MaxUint64),
202+
typ:int64(0),
203+
ok:false,// overflow
204+
},
205+
{
206+
name:"uint64(math.MaxInt16) to int16",
207+
value:uint64(math.MaxInt16),
208+
typ:int64(0),
209+
ok:true,
210+
expected:int64(math.MaxInt16),
211+
},
212+
// From int to int.
213+
{
214+
name:"int64(math.MaxInt64) to int16",
215+
value:int64(math.MaxInt64),
216+
typ:int16(0),
217+
ok:false,// overflow
218+
},
219+
{
220+
name:"int64(math.MaxInt16) to int",
221+
value:int64(math.MaxInt16),
222+
typ:int(0),
223+
ok:true,
224+
expected:int(math.MaxInt16),
225+
},
226+
227+
{
228+
name:"int64(math.MaxInt16) to int",
229+
value:int64(math.MaxInt16),
230+
typ:int(0),
231+
ok:true,
232+
expected:int(math.MaxInt16),
233+
},
234+
} {
235+
236+
v,ok:=ConvertIfPossible(reflect.ValueOf(test.value),reflect.TypeOf(test.typ))
237+
c.Assert(ok,qt.Equals,test.ok,qt.Commentf("test case: %s",test.name))
238+
iftest.ok {
239+
c.Assert(v.Interface(),qt.Equals,test.expected,qt.Commentf("test case: %s",test.name))
240+
}
241+
}
242+
}

‎tpl/internal/go_templates/texttemplate/exec.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -889,7 +889,7 @@ func canBeNil(typ reflect.Type) bool {
889889
}
890890

891891
// validateType guarantees that the value is valid and assignable to the type.
892-
func (s*state)validateType(value reflect.Value,typ reflect.Type) reflect.Value {
892+
func (s*state)_validateType(value reflect.Value,typ reflect.Type) reflect.Value {
893893
if!value.IsValid() {
894894
iftyp==nil {
895895
// An untyped nil interface{}. Accept as a proper nil value.

‎tpl/internal/go_templates/texttemplate/hugo_template.go‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,55 @@ func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node
431431
returnvv
432432
}
433433

434+
// validateType guarantees that the value is valid and assignable to the type.
435+
func (s*state)validateType(value reflect.Value,typ reflect.Type) reflect.Value {
436+
if!value.IsValid() {
437+
iftyp==nil {
438+
// An untyped nil interface{}. Accept as a proper nil value.
439+
returnreflect.ValueOf(nil)
440+
}
441+
ifcanBeNil(typ) {
442+
// Like above, but use the zero value of the non-nil type.
443+
returnreflect.Zero(typ)
444+
}
445+
s.errorf("invalid value; expected %s",typ)
446+
}
447+
iftyp==reflectValueType&&value.Type()!=typ {
448+
returnreflect.ValueOf(value)
449+
}
450+
iftyp!=nil&&!value.Type().AssignableTo(typ) {
451+
ifvalue.Kind()==reflect.Interface&&!value.IsNil() {
452+
value=value.Elem()
453+
ifvalue.Type().AssignableTo(typ) {
454+
returnvalue
455+
}
456+
// fallthrough
457+
}
458+
// Does one dereference or indirection work? We could do more, as we
459+
// do with method receivers, but that gets messy and method receivers
460+
// are much more constrained, so it makes more sense there than here.
461+
// Besides, one is almost always all you need.
462+
switch {
463+
casevalue.Kind()==reflect.Pointer&&value.Type().Elem().AssignableTo(typ):
464+
value=value.Elem()
465+
if!value.IsValid() {
466+
s.errorf("dereference of nil pointer of type %s",typ)
467+
}
468+
casereflect.PointerTo(value.Type()).AssignableTo(typ)&&value.CanAddr():
469+
value=value.Addr()
470+
default:
471+
// Added for Hugo.
472+
ifv,ok:=hreflect.ConvertIfPossible(value,typ);ok {
473+
value=v
474+
}else {
475+
s.errorf("wrong type for value; expected %s; got %s",typ,value.Type())
476+
}
477+
478+
}
479+
}
480+
returnvalue
481+
}
482+
434483
funcisTrue(val reflect.Value) (truth,okbool) {
435484
returnhreflect.IsTruthfulValue(val),true
436485
}

‎tpl/templates/templates_integration_test.go‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,3 +315,23 @@ MyPartial.
315315
b:=hugolib.Test(t,files)
316316
b.AssertFileContent("public/index.html","P1: true|P1: true|")
317317
}
318+
319+
funcTestYAMLAddDateIssue14079(t*testing.T) {
320+
t.Parallel()
321+
322+
files:=`
323+
-- hugo.toml --
324+
disableKinds = ["page", "section", "taxonomy", "term", "sitemap", "RSS"]
325+
-- assets/mydata.yaml --
326+
myinteger: 2
327+
-- layouts/all.html --
328+
{{ $date := "2023-10-15T13:18:50-07:00" | time }}
329+
{{ $mydata := resources.Get "mydata.yaml" | transform.Unmarshal }}
330+
date: {{ $date | time.Format "2006-01-06" }}|
331+
date+2y: {{ $date.AddDate $mydata.myinteger 0 0 | time.Format "2006-01-06" }}|
332+
`
333+
334+
b:=hugolib.Test(t,files)
335+
336+
b.AssertFileContent("public/index.html","date: 2023-10-23|","date+2y: 2025-10-25|")
337+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp