return f, realFilename, nil
}
-// Cause returns the underlying error or itself if it does not implement Unwrap.
+// Cause returns the underlying error, that is,
+// it unwraps errors until it finds one that does not implement
+// the Unwrap method.
+// For a shallow variant, see Unwrap.
func Cause(err error) error {
+ type unwrapper interface {
+ Unwrap() error
+ }
+
+ for err != nil {
+ cause, ok := err.(unwrapper)
+ if !ok {
+ break
+ }
+ err = cause.Unwrap()
+ }
+ return err
+}
+
+// Unwrap returns the underlying error or itself if it does not implement Unwrap.
+func Unwrap(err error) error {
if u := errors.Unwrap(err); u != nil {
return u
}
}
func extractFileTypePos(err error) (string, text.Position) {
- err = Cause(err)
+ err = Unwrap(err)
var fileType string
siteRenderContext := &siteRenderContext{cfg: config, multihost: h.Configs.IsMultihost}
+ renderErr := func(err error) error {
+ if err == nil {
+ return nil
+ }
+ if strings.Contains(err.Error(), "can't evaluate field Err in type resource.Resource") {
+ // In Hugo 0.141.0 we replaced the special error handling for resources.GetRemote
+ // with the more general try.
+ return fmt.Errorf("%s: Resource.Err was removed in Hugo v0.141.0 and replaced with a new try keyword, see https://gohugo.io/functions/go-template/try/", err)
+ }
+ return err
+ }
+
i := 0
for _, s := range h.Sites {
segmentFilter := s.conf.C.SegmentFilter
}
} else {
if err := s.render(siteRenderContext); err != nil {
- return err
+ return renderErr(err)
}
}
loggers.TimeTrackf(ll, start, nil, "")
return p.IsNode()
}
-func (p *pageState) Err() resource.ResourceError {
- return nil
-}
-
// Eq returns whether the current page equals the given page.
// This is what's invoked when doing `{{ if eq $page $otherPage }}`
func (p *pageState) Eq(other any) bool {
CSS integrity Data first: {{ $cssFingerprinted1.Data.Integrity }} {{ $cssFingerprinted1.RelPermalink }}
CSS integrity Data last: {{ $cssFingerprinted2.RelPermalink }} {{ $cssFingerprinted2.Data.Integrity }}
-{{ $failedImg := resources.GetRemote "%[1]s/fail.jpg" }}
+{{ $failedImg := try (resources.GetRemote "%[1]s/fail.jpg") }}
{{ $rimg := resources.GetRemote "%[1]s/sunset.jpg" }}
{{ $remotenotfound := resources.GetRemote "%[1]s/notfound.jpg" }}
{{ $localnotfound := resources.Get "images/notfound.jpg" }}
-{{ $gopherprotocol := resources.GetRemote "gopher://example.org" }}
+{{ $gopherprotocol := try (resources.GetRemote "gopher://example.org") }}
{{ $rfit := $rimg.Fit "200x200" }}
{{ $rfit2 := $rfit.Fit "100x200" }}
{{ $rimg = $rimg | fingerprint }}
FIT REMOTE: {{ $rfit.Name }}|{{ $rfit.RelPermalink }}|{{ $rfit.Width }}
REMOTE NOT FOUND: {{ if $remotenotfound }}FAILED{{ else}}OK{{ end }}
LOCAL NOT FOUND: {{ if $localnotfound }}FAILED{{ else}}OK{{ end }}
-PRINT PROTOCOL ERROR1: {{ with $gopherprotocol }}{{ . | safeHTML }}{{ end }}
+PRINT PROTOCOL ERROR1: {{ with $gopherprotocol }}{{ .Value | safeHTML }}{{ end }}
PRINT PROTOCOL ERROR2: {{ with $gopherprotocol }}{{ .Err | safeHTML }}{{ end }}
-PRINT PROTOCOL ERROR DETAILS: {{ with $gopherprotocol }}Err: {{ .Err | safeHTML }}{{ with .Err }}|{{ with .Data }}Body: {{ .Body }}|StatusCode: {{ .StatusCode }}{{ end }}|{{ end }}{{ end }}
-FAILED REMOTE ERROR DETAILS CONTENT: {{ with $failedImg.Err }}|{{ . }}|{{ with .Data }}Body: {{ .Body }}|StatusCode: {{ .StatusCode }}|ContentLength: {{ .ContentLength }}|ContentType: {{ .ContentType }}{{ end }}{{ end }}|
+PRINT PROTOCOL ERROR DETAILS: {{ with $gopherprotocol }}{{ with .Err }}Err: {{ . | safeHTML }}{{ with .Cause }}|{{ with .Data }}Body: {{ .Body }}|StatusCode: {{ .StatusCode }}{{ end }}|{{ end }}{{ end }}{{ end }}
+FAILED REMOTE ERROR DETAILS CONTENT: {{ with $failedImg }}{{ with .Err }}{{ with .Cause }}{{ . }}|{{ with .Data }}Body: {{ .Body }}|StatusCode: {{ .StatusCode }}|ContentLength: {{ .ContentLength }}|ContentType: {{ .ContentType }}{{ end }}{{ end }}{{ end }}{{ end }}|
`, ts.URL))
fs := b.Fs.Source
FIT REMOTE: /sunset_%[1]s.jpg|/sunset_%[1]s_hu15210517121918042184.jpg|200
REMOTE NOT FOUND: OK
LOCAL NOT FOUND: OK
-PRINT PROTOCOL ERROR DETAILS: Err: error calling resources.GetRemote: Get "gopher://example.org": unsupported protocol scheme "gopher"||
-FAILED REMOTE ERROR DETAILS CONTENT: |failed to fetch remote resource from '%[2]s/fail.jpg': Not Implemented|Body: { msg: failed }
+PRINT PROTOCOL ERROR DETAILS: Err: template: index.html:22:36: executing "index.html" at <resources.GetRemote>: error calling GetRemote: Get "gopher://example.org": unsupported protocol scheme "gopher"|
+FAILED REMOTE ERROR DETAILS CONTENT: failed to fetch remote resource from '%[2]s/fail.jpg': Not Implemented|Body: { msg: failed }
|StatusCode: 501|ContentLength: 16|ContentType: text/plain; charset=utf-8|
+++ /dev/null
-// Copyright 2021 The Hugo Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package resources
-
-import (
- "context"
- "image"
-
- "github.com/gohugoio/hugo/common/hugio"
- "github.com/gohugoio/hugo/common/maps"
- "github.com/gohugoio/hugo/media"
- "github.com/gohugoio/hugo/resources/images"
- "github.com/gohugoio/hugo/resources/images/exif"
- "github.com/gohugoio/hugo/resources/resource"
-)
-
-var (
- _ error = (*errorResource)(nil)
- // Image covers all current Resource implementations.
- _ images.ImageResource = (*errorResource)(nil)
- // The list of user facing and exported interfaces in resource.go
- // Note that if we're missing some interface here, the user will still
- // get an error, but not as pretty.
- _ resource.ContentResource = (*errorResource)(nil)
- _ resource.ReadSeekCloserResource = (*errorResource)(nil)
- _ resource.ResourcesLanguageMerger = (*resource.Resources)(nil)
- // Make sure it also fails when passed to a pipe function.
- _ ResourceTransformer = (*errorResource)(nil)
-)
-
-// NewErrorResource wraps err in a Resource where all but the Err method will panic.
-func NewErrorResource(err resource.ResourceError) resource.Resource {
- return &errorResource{ResourceError: err}
-}
-
-type errorResource struct {
- resource.ResourceError
-}
-
-func (e *errorResource) Err() resource.ResourceError {
- return e.ResourceError
-}
-
-func (e *errorResource) ReadSeekCloser() (hugio.ReadSeekCloser, error) {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) Content(context.Context) (any, error) {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) ResourceType() string {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) MediaType() media.Type {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) Permalink() string {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) RelPermalink() string {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) Name() string {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) Title() string {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) Params() maps.Params {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) Data() any {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) Height() int {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) Width() int {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) Process(spec string) (images.ImageResource, error) {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) Crop(spec string) (images.ImageResource, error) {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) Fill(spec string) (images.ImageResource, error) {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) Fit(spec string) (images.ImageResource, error) {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) Resize(spec string) (images.ImageResource, error) {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) Filter(filters ...any) (images.ImageResource, error) {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) Exif() *exif.ExifInfo {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) Colors() ([]images.Color, error) {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) DecodeImage() (image.Image, error) {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) Transform(...ResourceTransformation) (ResourceTransformer, error) {
- panic(e.ResourceError)
-}
-
-func (e *errorResource) TransformWithContext(context.Context, ...ResourceTransformation) (ResourceTransformer, error) {
- panic(e.ResourceError)
-}
var noOpPathInfo = media.DefaultPathParser.Parse(files.ComponentFolderContent, "no-op.md")
-func (p *nopPage) Err() resource.ResourceError {
- return nil
-}
-
func (p *nopPage) Aliases() []string {
return nil
}
sectionEntries []string
}
-func (p *testPage) Err() resource.ResourceError {
- return nil
-}
-
func (p *testPage) Aliases() []string {
panic("testpage: not implemented")
}
// Copy copies r to the targetPath given.
func Copy(r resource.Resource, targetPath string) resource.Resource {
- if r.Err() != nil {
- panic(fmt.Sprintf("Resource has an .Err: %s", r.Err()))
- }
return r.(resourceCopier).cloneTo(targetPath)
}
return hugio.ReadString(r)
}
-func (r *genericResource) Err() resource.ResourceError {
- return nil
-}
-
func (l *genericResource) Data() any {
return l.sd.Data
}
// NewResourceError creates a new ResourceError.
func NewResourceError(err error, data any) ResourceError {
+ if data == nil {
+ data = map[string]any{}
+ }
return &resourceError{
error: err,
data: data,
ResourceDataProvider
}
-// ErrProvider provides an Err.
-type ErrProvider interface {
- // Err returns an error if this resource is in an error state.
- // This will currently only be set for resources obtained from resources.GetRemote.
- Err() ResourceError
-}
-
// Resource represents a linkable resource, i.e. a content page, image etc.
type Resource interface {
ResourceWithoutMeta
MediaTypeProvider
ResourceLinksProvider
ResourceDataProvider
- ErrProvider
}
type ResourceWrapper interface {
[security.http]
methods = ['(?i)GET|POST|HEAD']
urls = ['.*gohugo\.io.*']
-
-- layouts/index.html --
{{ $url := "https://gohugo.io/img/hugo.png" }}
{{ $opts := dict "method" "head" }}
-{{ with resources.GetRemote $url $opts }}
+{{ with try (resources.GetRemote $url $opts) }}
{{ with .Err }}
{{ errorf "Unable to get remote resource: %s" . }}
- {{ else }}
+ {{ else with .Value }}
Head Content: {{ .Content }}. Head Data: {{ .Data }}
- {{ end }}
-{{ else }}
+ {{ else }}
{{ errorf "Unable to get remote resource: %s" $url }}
+ {{ end }}
{{ end }}
`
-- layouts/_default/single.html --
{{ $url := printf "%s%s" "URL" .RelPermalink}}
{{ $opts := dict }}
-{{ with resources.GetRemote $url $opts }}
+{{ with try (resources.GetRemote $url $opts) }}
{{ with .Err }}
- {{ errorf "Got Err: %s. Data: %v" . .Data }}
- {{ else }}
+ {{ errorf "Got Err: %s" . }}
+ {{ with .Cause }}{{ errorf "Data: %s" .Data }}{{ end }}
+ {{ else with .Value }}
Content: {{ .Content }}
+ {{ else }}
+ {{ errorf "Unable to get remote resource: %s" $url }}
{{ end }}
-{{ else }}
- {{ errorf "Unable to get remote resource: %s" $url }}
{{ end }}
`
return r.target.Content(ctx)
}
-func (r *resourceAdapter) Err() resource.ResourceError {
- return nil
-}
-
func (r *resourceAdapter) GetIdentity() identity.Identity {
return identity.FirstIdentity(r.target)
}
"io"
"reflect"
+ "github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/hreflect"
"github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse"
panic("not reached")
}
+// newErrorWithCause creates a new error with the given cause.
+func newErrorWithCause(err error) *TryError {
+ return &TryError{Err: err, Cause: herrors.Cause(err)}
+}
+
+// TryError wraps an error with a cause.
+type TryError struct {
+ Err error
+ Cause error
+}
+
+func (e *TryError) Error() string {
+ return e.Err.Error()
+}
+
+func (e *TryError) Unwrap() error {
+ return e.Err
+}
+
// 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
+ Err *TryError
}
// evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so
if name == "try" {
defer func() {
if r := recover(); r != nil {
+ // Cause: herrors.Cause(err)
if err, ok := r.(error); ok {
- val = reflect.ValueOf(TryValue{nil, err})
+ val = reflect.ValueOf(TryValue{Value: nil, Err: newErrorWithCause(err)})
} else {
- val = reflect.ValueOf(TryValue{nil, fmt.Errorf("%v", r)})
+ val = reflect.ValueOf(TryValue{Value: nil, Err: newErrorWithCause(fmt.Errorf("%v", r))})
}
}
}()
// Added for Hugo.
if name == "try" {
- return reflect.ValueOf(TryValue{vv.Interface(), nil})
+ return reflect.ValueOf(TryValue{Value: vv.Interface()})
}
return vv
//
// Note: This method does not return any error as a second return value,
// for any error situations the error can be checked in .Err.
-func (ns *Namespace) GetRemote(args ...any) resource.Resource {
+func (ns *Namespace) GetRemote(args ...any) (resource.Resource, error) {
get := func(args ...any) (resource.Resource, error) {
- if len(args) < 1 {
- return nil, errors.New("must provide an URL")
- }
-
- if len(args) > 2 {
- return nil, errors.New("must not provide more arguments than URL and options")
+ if len(args) < 1 || len(args) > 2 {
+ return nil, errors.New("must provide an URL and optionally an options map")
}
urlstr, err := cast.ToStringE(args[0])
if err != nil {
switch v := err.(type) {
case *create.HTTPError:
- return resources.NewErrorResource(resource.NewResourceError(v, v.Data))
+ return nil, resource.NewResourceError(v, v.Data)
default:
- return resources.NewErrorResource(resource.NewResourceError(fmt.Errorf("error calling resources.GetRemote: %w", err), make(map[string]any)))
+ return nil, resource.NewResourceError(err, nil)
}
}
- return r
+ return r, nil
}
// GetMatch finds the first Resource matching the given pattern, or nil if none found.
{{- $url := printf "https://twitter.com/%v/status/%v" .user .id -}}
{{- $query := querify "url" $url "dnt" .dnt -}}
{{- $request := printf "https://publish.twitter.com/oembed?%s" $query -}}
- {{- with resources.GetRemote $request -}}
+ {{- with try (resources.GetRemote $request) -}}
{{- with .Err -}}
{{- errorf "%s" . -}}
- {{- else -}}
+ {{- else with .Value -}}
{{- (. | transform.Unmarshal).html | safeHTML -}}
- {{- end -}}
- {{- else -}}
+ {{- else -}}
{{- warnidf "shortcode-twitter-getremote" "The %q shortcode was unable to retrieve the remote data. See %s" .name .position -}}
+ {{- end -}}
{{- end -}}
{{- end -}}
{{- $url := printf "https://twitter.com/%v/status/%v" .user .id -}}
{{- $query := querify "url" $url "dnt" .dnt "omit_script" true -}}
{{- $request := printf "https://publish.twitter.com/oembed?%s" $query -}}
- {{- with resources.GetRemote $request -}}
+ {{- with try (resources.GetRemote $request) -}}
{{- with .Err -}}
{{- errorf "%s" . -}}
- {{- else -}}
+ {{- else with .Value -}}
{{- (. | transform.Unmarshal).html | safeHTML -}}
+ {{- else -}}
+ {{- warnidf "shortcode-twitter-simple-getremote" "The %q shortcode was unable to retrieve the remote data. See %s" .name .position -}}
{{- end -}}
- {{- else -}}
- {{- warnidf "shortcode-twitter-simple-getremote" "The %q shortcode was unable to retrieve the remote data. See %s" .name .position -}}
{{- end -}}
{{- end -}}
{{- .Page.Scratch.Set "__h_simple_twitter_css" true -}}
<style type="text/css">
.twitter-tweet {
- font: 14px/1.45 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
+ font:
+ 14px/1.45 -apple-system,
+ BlinkMacSystemFont,
+ "Segoe UI",
+ Roboto,
+ Oxygen-Sans,
+ Ubuntu,
+ Cantarell,
+ "Helvetica Neue",
+ sans-serif;
border-left: 4px solid #2b7bb9;
padding-left: 1.5em;
color: #555;
{{- $url := urls.JoinPath "https://vimeo.com" .id -}}
{{- $query := querify "url" $url "dnt" $dnt -}}
{{- $request := printf "https://vimeo.com/api/oembed.json?%s" $query -}}
- {{- with resources.GetRemote $request -}}
+ {{- with try (resources.GetRemote $request) -}}
{{- with .Err -}}
{{- errorf "%s" . -}}
- {{- else -}}
+ {{- else with .Value -}}
{{- with . | transform.Unmarshal -}}
{{- $class := printf "%s %s" "s_video_simple" "__h_video" -}}
{{- with $.class -}}
</a>
</div>
{{- end -}}
+ {{- else -}}
+ {{- warnidf "shortcode-vimeo-simple" "The %q shortcode was unable to retrieve the remote data. See %s" .name .position -}}
{{- end -}}
- {{- else -}}
- {{- warnidf "shortcode-vimeo-simple" "The %q shortcode was unable to retrieve the remote data. See %s" .name .position -}}
{{- end -}}
{{- end -}}