github.com/neohugo/neohugo@v0.123.8/resources/postpub/postpub.go (about)

     1  // Copyright 2020 The Hugo Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package postpub
    15  
    16  import (
    17  	"context"
    18  	"fmt"
    19  	"reflect"
    20  	"strconv"
    21  	"strings"
    22  
    23  	"github.com/spf13/cast"
    24  
    25  	"github.com/neohugo/neohugo/common/hreflect"
    26  	"github.com/neohugo/neohugo/common/maps"
    27  	"github.com/neohugo/neohugo/media"
    28  	"github.com/neohugo/neohugo/resources/resource"
    29  )
    30  
    31  type PostPublishedResource interface {
    32  	resource.ResourceTypeProvider
    33  	resource.ResourceLinksProvider
    34  	resource.ResourceNameTitleProvider
    35  	resource.ResourceParamsProvider
    36  	resource.ResourceDataProvider
    37  	resource.OriginProvider
    38  
    39  	MediaType() map[string]any
    40  }
    41  
    42  const (
    43  	PostProcessPrefix = "__h_pp_l1"
    44  
    45  	// The suffix has an '=' in it to prevent the minifier to remove any enclosing
    46  	// quoutes around the attribute values.
    47  	// See issue #8884.
    48  	PostProcessSuffix = "__e="
    49  )
    50  
    51  func NewPostPublishResource(id int, r resource.Resource) PostPublishedResource {
    52  	return &PostPublishResource{
    53  		prefix:   PostProcessPrefix + "_" + strconv.Itoa(id) + "_",
    54  		delegate: r,
    55  	}
    56  }
    57  
    58  // PostPublishResource holds a Resource to be transformed post publishing.
    59  type PostPublishResource struct {
    60  	prefix   string
    61  	delegate resource.Resource
    62  }
    63  
    64  func (r *PostPublishResource) field(name string) string {
    65  	return r.prefix + name + PostProcessSuffix
    66  }
    67  
    68  func (r *PostPublishResource) Permalink() string {
    69  	return r.field("Permalink")
    70  }
    71  
    72  func (r *PostPublishResource) RelPermalink() string {
    73  	return r.field("RelPermalink")
    74  }
    75  
    76  func (r *PostPublishResource) Origin() resource.Resource {
    77  	return r.delegate
    78  }
    79  
    80  func (r *PostPublishResource) GetFieldString(pattern string) (string, bool) {
    81  	if r == nil {
    82  		panic("resource is nil")
    83  	}
    84  	prefixIdx := strings.Index(pattern, r.prefix)
    85  	if prefixIdx == -1 {
    86  		// Not a method on this resource.
    87  		return "", false
    88  	}
    89  
    90  	fieldAccessor := pattern[prefixIdx+len(r.prefix) : strings.Index(pattern, PostProcessSuffix)]
    91  
    92  	d := r.delegate
    93  	switch {
    94  	case fieldAccessor == "RelPermalink":
    95  		return d.RelPermalink(), true
    96  	case fieldAccessor == "Permalink":
    97  		return d.Permalink(), true
    98  	case fieldAccessor == "Name":
    99  		return d.Name(), true
   100  	case fieldAccessor == "Title":
   101  		return d.Title(), true
   102  	case fieldAccessor == "ResourceType":
   103  		return d.ResourceType(), true
   104  	case fieldAccessor == "Content":
   105  		content, err := d.(resource.ContentProvider).Content(context.Background())
   106  		if err != nil {
   107  			return "", true
   108  		}
   109  		return cast.ToString(content), true
   110  	case strings.HasPrefix(fieldAccessor, "MediaType"):
   111  		return r.fieldToString(d.MediaType(), fieldAccessor), true
   112  	case fieldAccessor == "Data.Integrity":
   113  		return cast.ToString((d.Data().(map[string]any)["Integrity"])), true
   114  	default:
   115  		panic(fmt.Sprintf("unknown field accessor %q", fieldAccessor))
   116  	}
   117  }
   118  
   119  func (r *PostPublishResource) fieldToString(receiver any, path string) string {
   120  	fieldname := strings.Split(path, ".")[1]
   121  
   122  	receiverv := reflect.ValueOf(receiver)
   123  	switch receiverv.Kind() {
   124  	case reflect.Map:
   125  		v := receiverv.MapIndex(reflect.ValueOf(fieldname))
   126  		return cast.ToString(v.Interface())
   127  	default:
   128  		v := receiverv.FieldByName(fieldname)
   129  		if !v.IsValid() {
   130  			method := hreflect.GetMethodByName(receiverv, fieldname)
   131  			if method.IsValid() {
   132  				vals := method.Call(nil)
   133  				if len(vals) > 0 {
   134  					v = vals[0]
   135  				}
   136  
   137  			}
   138  		}
   139  
   140  		if v.IsValid() {
   141  			return cast.ToString(v.Interface())
   142  		}
   143  		return ""
   144  	}
   145  }
   146  
   147  func (r *PostPublishResource) Data() any {
   148  	m := map[string]any{
   149  		"Integrity": "",
   150  	}
   151  	insertFieldPlaceholders("Data", m, r.field)
   152  	return m
   153  }
   154  
   155  func (r *PostPublishResource) MediaType() map[string]any {
   156  	m := structToMapWithPlaceholders("MediaType", media.Type{}, r.field)
   157  	return m
   158  }
   159  
   160  func (r *PostPublishResource) ResourceType() string {
   161  	return r.field("ResourceType")
   162  }
   163  
   164  func (r *PostPublishResource) Name() string {
   165  	return r.field("Name")
   166  }
   167  
   168  func (r *PostPublishResource) Title() string {
   169  	return r.field("Title")
   170  }
   171  
   172  func (r *PostPublishResource) Params() maps.Params {
   173  	panic(r.fieldNotSupported("Params"))
   174  }
   175  
   176  func (r *PostPublishResource) Content(context.Context) (any, error) {
   177  	return r.field("Content"), nil
   178  }
   179  
   180  func (r *PostPublishResource) fieldNotSupported(name string) string {
   181  	return fmt.Sprintf("method .%s is currently not supported in post-publish transformations.", name)
   182  }