github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/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  	"fmt"
    18  	"reflect"
    19  	"strconv"
    20  	"strings"
    21  
    22  	"github.com/spf13/cast"
    23  
    24  	"github.com/gohugoio/hugo/common/maps"
    25  	"github.com/gohugoio/hugo/media"
    26  	"github.com/gohugoio/hugo/resources/resource"
    27  )
    28  
    29  type PostPublishedResource interface {
    30  	resource.ResourceTypeProvider
    31  	resource.ResourceLinksProvider
    32  	resource.ResourceMetaProvider
    33  	resource.ResourceParamsProvider
    34  	resource.ResourceDataProvider
    35  	resource.OriginProvider
    36  
    37  	MediaType() map[string]interface{}
    38  }
    39  
    40  const (
    41  	PostProcessPrefix = "__h_pp_l1"
    42  
    43  	// The suffix has an '=' in it to prevent the minifier to remove any enclosing
    44  	// quoutes around the attribute values.
    45  	// See issue #8884.
    46  	PostProcessSuffix = "__e="
    47  )
    48  
    49  func NewPostPublishResource(id int, r resource.Resource) PostPublishedResource {
    50  	return &PostPublishResource{
    51  		prefix:   PostProcessPrefix + "_" + strconv.Itoa(id) + "_",
    52  		delegate: r,
    53  	}
    54  }
    55  
    56  // postPublishResource holds a Resource to be transformed post publishing.
    57  type PostPublishResource struct {
    58  	prefix   string
    59  	delegate resource.Resource
    60  }
    61  
    62  func (r *PostPublishResource) field(name string) string {
    63  	return r.prefix + name + PostProcessSuffix
    64  }
    65  
    66  func (r *PostPublishResource) Permalink() string {
    67  	return r.field("Permalink")
    68  }
    69  
    70  func (r *PostPublishResource) RelPermalink() string {
    71  	return r.field("RelPermalink")
    72  }
    73  
    74  func (r *PostPublishResource) Origin() resource.Resource {
    75  	return r.delegate
    76  }
    77  
    78  func (r *PostPublishResource) GetFieldString(pattern string) (string, bool) {
    79  	if r == nil {
    80  		panic("resource is nil")
    81  	}
    82  	prefixIdx := strings.Index(pattern, r.prefix)
    83  	if prefixIdx == -1 {
    84  		// Not a method on this resource.
    85  		return "", false
    86  	}
    87  
    88  	fieldAccessor := pattern[prefixIdx+len(r.prefix) : strings.Index(pattern, PostProcessSuffix)]
    89  
    90  	d := r.delegate
    91  	switch {
    92  	case fieldAccessor == "RelPermalink":
    93  		return d.RelPermalink(), true
    94  	case fieldAccessor == "Permalink":
    95  		return d.Permalink(), true
    96  	case fieldAccessor == "Name":
    97  		return d.Name(), true
    98  	case fieldAccessor == "Title":
    99  		return d.Title(), true
   100  	case fieldAccessor == "ResourceType":
   101  		return d.ResourceType(), true
   102  	case fieldAccessor == "Content":
   103  		content, err := d.(resource.ContentProvider).Content()
   104  		if err != nil {
   105  			return "", true
   106  		}
   107  		return cast.ToString(content), true
   108  	case strings.HasPrefix(fieldAccessor, "MediaType"):
   109  		return r.fieldToString(d.MediaType(), fieldAccessor), true
   110  	case fieldAccessor == "Data.Integrity":
   111  		return cast.ToString((d.Data().(map[string]interface{})["Integrity"])), true
   112  	default:
   113  		panic(fmt.Sprintf("unknown field accessor %q", fieldAccessor))
   114  	}
   115  }
   116  
   117  func (r *PostPublishResource) fieldToString(receiver interface{}, path string) string {
   118  	fieldname := strings.Split(path, ".")[1]
   119  
   120  	receiverv := reflect.ValueOf(receiver)
   121  	switch receiverv.Kind() {
   122  	case reflect.Map:
   123  		v := receiverv.MapIndex(reflect.ValueOf(fieldname))
   124  		return cast.ToString(v.Interface())
   125  	default:
   126  		v := receiverv.FieldByName(fieldname)
   127  		if !v.IsValid() {
   128  			method := receiverv.MethodByName(fieldname)
   129  			if method.IsValid() {
   130  				vals := method.Call(nil)
   131  				if len(vals) > 0 {
   132  					v = vals[0]
   133  				}
   134  
   135  			}
   136  		}
   137  
   138  		if v.IsValid() {
   139  			return cast.ToString(v.Interface())
   140  		}
   141  		return ""
   142  	}
   143  }
   144  
   145  func (r *PostPublishResource) Data() interface{} {
   146  	m := map[string]interface{}{
   147  		"Integrity": "",
   148  	}
   149  	insertFieldPlaceholders("Data", m, r.field)
   150  	return m
   151  }
   152  
   153  func (r *PostPublishResource) MediaType() map[string]interface{} {
   154  	m := structToMapWithPlaceholders("MediaType", media.Type{}, r.field)
   155  	return m
   156  }
   157  
   158  func (r *PostPublishResource) ResourceType() string {
   159  	return r.field("ResourceType")
   160  }
   161  
   162  func (r *PostPublishResource) Name() string {
   163  	return r.field("Name")
   164  }
   165  
   166  func (r *PostPublishResource) Title() string {
   167  	return r.field("Title")
   168  }
   169  
   170  func (r *PostPublishResource) Params() maps.Params {
   171  	panic(r.fieldNotSupported("Params"))
   172  }
   173  
   174  func (r *PostPublishResource) Content() (interface{}, error) {
   175  	return r.field("Content"), nil
   176  }
   177  
   178  func (r *PostPublishResource) fieldNotSupported(name string) string {
   179  	return fmt.Sprintf("method .%s is currently not supported in post-publish transformations.", name)
   180  }