hugo/hugolib/content_map.go
2024-04-11 09:23:17 +02:00

273 lines
5.5 KiB
Go

// Copyright 2019 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 hugolib
import (
"fmt"
"path"
"path/filepath"
"strings"
"unicode"
"github.com/bep/logg"
"github.com/gohugoio/hugo/common/hugio"
"github.com/gohugoio/hugo/common/paths"
"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/source"
"github.com/gohugoio/hugo/resources/page"
"github.com/gohugoio/hugo/resources/resource"
"github.com/gohugoio/hugo/hugofs"
)
// Used to mark ambiguous keys in reverse index lookups.
var ambiguousContentNode = &pageState{}
var trimCutsetDotSlashSpace = func(r rune) bool {
return r == '.' || r == '/' || unicode.IsSpace(r)
}
type contentMapConfig struct {
lang string
taxonomyConfig taxonomiesConfigValues
taxonomyDisabled bool
taxonomyTermDisabled bool
pageDisabled bool
isRebuild bool
}
var _ contentNodeI = (*resourceSource)(nil)
type resourceSource struct {
path *paths.Path
opener hugio.OpenReadSeekCloser
fi hugofs.FileMetaInfo
r resource.Resource
}
func (r resourceSource) clone() *resourceSource {
r.r = nil
return &r
}
func (r *resourceSource) LangIndex() int {
if r.r != nil && r.isPage() {
return r.r.(*pageState).s.languagei
}
return r.fi.Meta().LangIndex
}
func (r *resourceSource) MarkStale() {
resource.MarkStale(r.r)
}
func (r *resourceSource) resetBuildState() {
if rr, ok := r.r.(buildStateReseter); ok {
rr.resetBuildState()
}
}
func (r *resourceSource) isPage() bool {
_, ok := r.r.(page.Page)
return ok
}
func (r *resourceSource) GetIdentity() identity.Identity {
if r.r != nil {
return r.r.(identity.IdentityProvider).GetIdentity()
}
return r.path
}
func (r *resourceSource) ForEeachIdentity(f func(identity.Identity) bool) bool {
return f(r.GetIdentity())
}
func (r *resourceSource) Path() string {
return r.path.Path()
}
func (r *resourceSource) isContentNodeBranch() bool {
return false
}
var _ contentNodeI = (*resourceSources)(nil)
type resourceSources []*resourceSource
func (n resourceSources) MarkStale() {
for _, r := range n {
if r != nil {
r.MarkStale()
}
}
}
func (n resourceSources) Path() string {
panic("not supported")
}
func (n resourceSources) isContentNodeBranch() bool {
return false
}
func (n resourceSources) resetBuildState() {
for _, r := range n {
if r != nil {
r.resetBuildState()
}
}
}
func (n resourceSources) GetIdentity() identity.Identity {
for _, r := range n {
if r != nil {
return r.GetIdentity()
}
}
return nil
}
func (n resourceSources) ForEeachIdentity(f func(identity.Identity) bool) bool {
for _, r := range n {
if r != nil {
if f(r.GetIdentity()) {
return true
}
}
}
return false
}
func (cfg contentMapConfig) getTaxonomyConfig(s string) (v viewName) {
for _, n := range cfg.taxonomyConfig.views {
if strings.HasPrefix(s, n.pluralTreeKey) {
return n
}
}
return
}
func (m *pageMap) AddFi(fi hugofs.FileMetaInfo) error {
if fi.IsDir() {
return nil
}
insertResource := func(fim hugofs.FileMetaInfo) error {
pi := fi.Meta().PathInfo
key := pi.Base()
tree := m.treeResources
commit := tree.Lock(true)
defer commit()
r := func() (hugio.ReadSeekCloser, error) {
return fim.Meta().Open()
}
var rs *resourceSource
if pi.IsContent() {
// Create the page now as we need it at assembly time.
// The other resources are created if needed.
pageResource, pi, err := m.s.h.newPage(
&pageMeta{
f: source.NewFileInfo(fim),
pathInfo: pi,
bundled: true,
},
)
if err != nil {
return err
}
if pageResource == nil {
// Disabled page.
return nil
}
key = pi.Base()
rs = &resourceSource{r: pageResource}
} else {
rs = &resourceSource{path: pi, opener: r, fi: fim}
}
tree.InsertIntoValuesDimension(key, rs)
return nil
}
meta := fi.Meta()
pi := meta.PathInfo
switch pi.BundleType() {
case paths.PathTypeFile, paths.PathTypeContentResource:
m.s.Log.Trace(logg.StringFunc(
func() string {
return fmt.Sprintf("insert resource: %q", fi.Meta().Filename)
},
))
if err := insertResource(fi); err != nil {
return err
}
default:
m.s.Log.Trace(logg.StringFunc(
func() string {
return fmt.Sprintf("insert bundle: %q", fi.Meta().Filename)
},
))
// A content file.
p, pi, err := m.s.h.newPage(
&pageMeta{
f: source.NewFileInfo(fi),
pathInfo: pi,
bundled: false,
},
)
if err != nil {
return err
}
if p == nil {
// Disabled page.
return nil
}
m.treePages.InsertWithLock(pi.Base(), p)
}
return nil
}
// The home page is represented with the zero string.
// All other keys starts with a leading slash. No trailing slash.
// Slashes are Unix-style.
func cleanTreeKey(elem ...string) string {
var s string
if len(elem) > 0 {
s = elem[0]
if len(elem) > 1 {
s = path.Join(elem...)
}
}
s = strings.TrimFunc(s, trimCutsetDotSlashSpace)
s = filepath.ToSlash(strings.ToLower(paths.Sanitize(s)))
if s == "" || s == "/" {
return ""
}
if s[0] != '/' {
s = "/" + s
}
return s
}