github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/resources/resource/resources.go (about)

     1  // Copyright 2023 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 resource contains Resource related types.
    15  package resource
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  
    21  	"github.com/gohugoio/hugo/hugofs/glob"
    22  	"github.com/spf13/cast"
    23  )
    24  
    25  var _ ResourceFinder = (*Resources)(nil)
    26  
    27  // Resources represents a slice of resources, which can be a mix of different types.
    28  // I.e. both pages and images etc.
    29  type Resources []Resource
    30  
    31  // var _ resource.ResourceFinder = (*Namespace)(nil)
    32  // ResourcesConverter converts a given slice of Resource objects to Resources.
    33  type ResourcesConverter interface {
    34  	// For internal use.
    35  	ToResources() Resources
    36  }
    37  
    38  // ByType returns resources of a given resource type (e.g. "image").
    39  func (r Resources) ByType(typ any) Resources {
    40  	tpstr, err := cast.ToStringE(typ)
    41  	if err != nil {
    42  		panic(err)
    43  	}
    44  	var filtered Resources
    45  
    46  	for _, resource := range r {
    47  		if resource.ResourceType() == tpstr {
    48  			filtered = append(filtered, resource)
    49  		}
    50  	}
    51  	return filtered
    52  }
    53  
    54  // Get locates the name given in Resources.
    55  // The search is case insensitive.
    56  func (r Resources) Get(name any) Resource {
    57  	namestr, err := cast.ToStringE(name)
    58  	if err != nil {
    59  		panic(err)
    60  	}
    61  	namestr = strings.ToLower(namestr)
    62  	for _, resource := range r {
    63  		if strings.EqualFold(namestr, resource.Name()) {
    64  			return resource
    65  		}
    66  	}
    67  	return nil
    68  }
    69  
    70  // GetMatch finds the first Resource matching the given pattern, or nil if none found.
    71  // See Match for a more complete explanation about the rules used.
    72  func (r Resources) GetMatch(pattern any) Resource {
    73  	patternstr, err := cast.ToStringE(pattern)
    74  	if err != nil {
    75  		panic(err)
    76  	}
    77  
    78  	g, err := glob.GetGlob(patternstr)
    79  	if err != nil {
    80  		panic(err)
    81  	}
    82  
    83  	for _, resource := range r {
    84  		if g.Match(strings.ToLower(resource.Name())) {
    85  			return resource
    86  		}
    87  	}
    88  
    89  	return nil
    90  }
    91  
    92  // Match gets all resources matching the given base filename prefix, e.g
    93  // "*.png" will match all png files. The "*" does not match path delimiters (/),
    94  // so if you organize your resources in sub-folders, you need to be explicit about it, e.g.:
    95  // "images/*.png". To match any PNG image anywhere in the bundle you can do "**.png", and
    96  // to match all PNG images below the images folder, use "images/**.jpg".
    97  // The matching is case insensitive.
    98  // Match matches by using the value of Resource.Name, which, by default, is a filename with
    99  // path relative to the bundle root with Unix style slashes (/) and no leading slash, e.g. "images/logo.png".
   100  // See https://github.com/gobwas/glob for the full rules set.
   101  func (r Resources) Match(pattern any) Resources {
   102  	patternstr, err := cast.ToStringE(pattern)
   103  	if err != nil {
   104  		panic(err)
   105  	}
   106  
   107  	g, err := glob.GetGlob(patternstr)
   108  	if err != nil {
   109  		panic(err)
   110  	}
   111  
   112  	var matches Resources
   113  	for _, resource := range r {
   114  		if g.Match(strings.ToLower(resource.Name())) {
   115  			matches = append(matches, resource)
   116  		}
   117  	}
   118  	return matches
   119  }
   120  
   121  type translatedResource interface {
   122  	TranslationKey() string
   123  }
   124  
   125  // MergeByLanguage adds missing translations in r1 from r2.
   126  func (r Resources) MergeByLanguage(r2 Resources) Resources {
   127  	result := append(Resources(nil), r...)
   128  	m := make(map[string]bool)
   129  	for _, rr := range r {
   130  		if translated, ok := rr.(translatedResource); ok {
   131  			m[translated.TranslationKey()] = true
   132  		}
   133  	}
   134  
   135  	for _, rr := range r2 {
   136  		if translated, ok := rr.(translatedResource); ok {
   137  			if _, found := m[translated.TranslationKey()]; !found {
   138  				result = append(result, rr)
   139  			}
   140  		}
   141  	}
   142  	return result
   143  }
   144  
   145  // MergeByLanguageInterface is the generic version of MergeByLanguage. It
   146  // is here just so it can be called from the tpl package.
   147  func (r Resources) MergeByLanguageInterface(in any) (any, error) {
   148  	r2, ok := in.(Resources)
   149  	if !ok {
   150  		return nil, fmt.Errorf("%T cannot be merged by language", in)
   151  	}
   152  	return r.MergeByLanguage(r2), nil
   153  }
   154  
   155  // Source is an internal template and not meant for use in the templates. It
   156  // may change without notice.
   157  type Source interface {
   158  	Publish() error
   159  }
   160  
   161  // ResourceFinder provides methods to find Resources.
   162  // Note that GetRemote (as found in resources.GetRemote) is
   163  // not covered by this interface, as this is only available as a global template function.
   164  type ResourceFinder interface {
   165  
   166  	// Get locates the Resource with the given name in the current context (e.g. in .Page.Resources).
   167  	//
   168  	// It returns nil if no Resource could found, panics if name is invalid.
   169  	Get(name any) Resource
   170  
   171  	// GetMatch finds the first Resource matching the given pattern, or nil if none found.
   172  	//
   173  	// See Match for a more complete explanation about the rules used.
   174  	//
   175  	// It returns nil if no Resource could found, panics if pattern is invalid.
   176  	GetMatch(pattern any) Resource
   177  
   178  	// Match gets all resources matching the given base path prefix, e.g
   179  	// "*.png" will match all png files. The "*" does not match path delimiters (/),
   180  	// so if you organize your resources in sub-folders, you need to be explicit about it, e.g.:
   181  	// "images/*.png". To match any PNG image anywhere in the bundle you can do "**.png", and
   182  	// to match all PNG images below the images folder, use "images/**.jpg".
   183  	//
   184  	// The matching is case insensitive.
   185  	//
   186  	// Match matches by using a relative pathwith Unix style slashes (/) and no
   187  	// leading slash, e.g. "images/logo.png".
   188  	//
   189  	// See https://github.com/gobwas/glob for the full rules set.
   190  	//
   191  	// See Match for a more complete explanation about the rules used.
   192  	//
   193  	// It returns nil if no Resources could found, panics if pattern is invalid.
   194  	Match(pattern any) Resources
   195  
   196  	// ByType returns resources of a given resource type (e.g. "image").
   197  	// It returns nil if no Resources could found, panics if typ is invalid.
   198  	ByType(typ any) Resources
   199  }