mirror of
https://github.com/gohugoio/hugo.git
synced 2025-04-25 21:21:22 +03:00
parent
723e3f4342
commit
5d2cbee989
4 changed files with 71 additions and 2 deletions
|
@ -15,6 +15,7 @@ package template
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
|
||||
|
@ -255,10 +256,32 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node,
|
|||
panic("not reached")
|
||||
}
|
||||
|
||||
// TryValue is what gets returned when using the "try" keyword.
|
||||
type TryValue struct {
|
||||
// Value is the value returned by the function or method wrapped with "try".
|
||||
// This will always be nil if Err is set.
|
||||
Value any
|
||||
// Err is the error returned by the function or method wrapped with "try".
|
||||
// This will always be nil if Value is set.
|
||||
Err error
|
||||
}
|
||||
|
||||
// evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so
|
||||
// it looks just like a function call. The arg list, if non-nil, includes (in the manner of the shell), arg[0]
|
||||
// as the function itself.
|
||||
func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node, name string, args []parse.Node, final reflect.Value, first ...reflect.Value) reflect.Value {
|
||||
func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node, name string, args []parse.Node, final reflect.Value, first ...reflect.Value) (val reflect.Value) {
|
||||
// Added for Hugo.
|
||||
if name == "try" {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if err, ok := r.(error); ok {
|
||||
val = reflect.ValueOf(TryValue{nil, err})
|
||||
} else {
|
||||
val = reflect.ValueOf(TryValue{nil, fmt.Errorf("%v", r)})
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
if args != nil {
|
||||
args = args[1:] // Zeroth arg is function name/node; not passed to function.
|
||||
}
|
||||
|
@ -371,6 +394,11 @@ func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node
|
|||
s.helper.OnCalled(s.ctx, s.prep, name, argv, vv)
|
||||
}
|
||||
|
||||
// Added for Hugo.
|
||||
if name == "try" {
|
||||
return reflect.ValueOf(TryValue{vv.Interface(), nil})
|
||||
}
|
||||
|
||||
return vv
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ func init() {
|
|||
},
|
||||
)
|
||||
|
||||
// TODO(bep) we need the return to be a valid identifier, but
|
||||
// TODO(bep) we need the return to be a valid identifiers, but
|
||||
// should consider another way of adding it.
|
||||
ns.AddMethodMapping(func() string { return "" },
|
||||
[]string{"return"},
|
||||
|
|
|
@ -70,6 +70,13 @@ func init() {
|
|||
},
|
||||
)
|
||||
|
||||
ns.AddMethodMapping(func(v any) (any, error) {
|
||||
return v, nil
|
||||
},
|
||||
[]string{"try"},
|
||||
[][2]string{},
|
||||
)
|
||||
|
||||
return ns
|
||||
}
|
||||
|
||||
|
|
|
@ -93,3 +93,37 @@ Home: true
|
|||
|
||||
`)
|
||||
}
|
||||
|
||||
func TestTry(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- config.toml --
|
||||
baseURL = 'http://example.com/'
|
||||
-- layouts/index.html --
|
||||
Home.
|
||||
{{ $g := try ("hello = \"Hello Hugo\"" | transform.Unmarshal) }}
|
||||
{{ with $g.Err }}
|
||||
Err1: {{ . }}
|
||||
{{ else }}
|
||||
Value1: {{ $g.Value.hello | safeHTML }}|
|
||||
{{ end }}
|
||||
{{ $g := try ("hello != \"Hello Hugo\"" | transform.Unmarshal) }}
|
||||
{{ with $g.Err }}
|
||||
Err2: {{ . | safeHTML }}
|
||||
{{ else }}
|
||||
Value2: {{ $g.Value.hello | safeHTML }}|
|
||||
{{ end }}
|
||||
Try upper: {{ (try ("hello" | upper)).Value }}
|
||||
Try printf: {{ (try (printf "hello %s" "world")).Value }}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html",
|
||||
"Value1: Hello Hugo|",
|
||||
"Err2: template: index.html:",
|
||||
"Try upper: HELLO",
|
||||
"Try printf: hello world",
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue