/
githubmirror
/
hugo
Обзор
Документация
Войти
/
githubmirror
/
hugo
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
hugolib/content_map_page_contentnode.go
474 строки
11 KB
Bjørn Erik Pedersen
Fix panic on server atomic save edits on MacOS
26 июл 2026, 15:00
26 июл 2026, 15:00
6bf1524
Код
Авторство
О чём код?
// Copyright 2025 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" "iter" "github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/hugolib/sitesmatrix" "github.com/gohugoio/hugo/identity" "github.com/gohugoio/hugo/resources/page" "github.com/gohugoio/hugo/resources/resource" ) var _ contentNode = (*contentNodeSeq)(nil) var ( _ contentNode = (*resourceSource)(nil) _ contentNodeContentWeightProvider = (*pageMetaSource)(nil) _ contentNodeContentWeightProvider = (*pageState)(nil) _ contentNodeForSites = (*pageState)(nil) _ contentNodePage = (*contentNodes)(nil) _ contentNodePage = (*pageMetaSource)(nil) _ contentNodePage = (*pageState)(nil) _ contentNodeSourceEntryIDProvider = (*pageState)(nil) _ contentNodeSourceEntryIDProvider = (*pageMetaSource)(nil) _ contentNodeSourceEntryIDProvider = (*resourceSource)(nil) _ contentNodeLookupContentNode = (*contentNodesMap)(nil) _ contentNodeLookupContentNode = (contentNodes)(nil) _ contentNodeSingle = (*pageMetaSource)(nil) _ contentNodeSingle = (*pageState)(nil) _ contentNodeSingle = (*resourceSource)(nil) ) var ( _ contentNode = (*contentNodesMap)(nil) _ contentNodeMap = (*contentNodesMap)(nil) ) func (n contentNodesMap) ForEeachInAllDimensions(f func(contentNode) bool) { for _, nn := range n { if !f(nn) { return } } } func (n contentNodesMap) Path() string { return n.sample().Path() } type ( contentNode interface { Path() string contentNodeForEach } contentNodeBuildStateResetter interface { resetBuildState() } contentNodeSeq2 iter.Seq2[sitesmatrix.Vector, contentNode] contentNodeForSite interface { contentNode siteVector() sitesmatrix.Vector } contentNodeForEach interface { // forEeachContentNode iterates over all content nodes. // calling f with the site vector and the content node. // If f returns false, the iteration stops. // It returns false if the iteration was stopped early. forEeachContentNode(f func(sitesmatrix.Vector, contentNode) bool) bool } contentNodeForSites interface { contentNode siteVectors() sitesmatrix.VectorIterator } contentNodePage interface { contentNode nodeCategoryPage() // Marker interface. } contentNodeSingle interface { contentNode nodeCategorySingle() // Marker interface. } contentNodeLookupContentNode interface { contentNode lookupContentNode(v sitesmatrix.Vector) contentNode } contentNodeLookupContentNodes interface { lookupContentNodes(v sitesmatrix.Vector, fallback bool) iter.Seq[contentNodeForSite] } contentNodeSampleProvider interface { // sample is used to get a sample contentNode from a collection. sample() contentNode } contentNodeCascadeProvider interface { getCascade() *page.PageMatcherParamsConfigs } contentNodeContentWeightProvider interface { contentWeight() int } contentNodeIsEmptyProvider interface { isEmpty() bool } contentNodeSourceEntryIDProvider interface { nodeSourceEntryID() any } contentNodeMap interface { contentNode contentNodeLookupContentNode } ) type contentNodeSeq iter.Seq[contentNode] func (n contentNodeSeq) Path() string { return n.sample().Path() } func (n contentNodeSeq) isEmpty() bool { for range n { return false } return true } func (n contentNodeSeq) forEeachContentNode(f func(sitesmatrix.Vector, contentNode) bool) bool { for nn := range n { if !nn.forEeachContentNode(f) { return false } } return true } func (n contentNodeSeq) sample() contentNode { for nn := range n { return nn } return nil } func (n contentNodeSeq2) forEeachContentNode(f func(sitesmatrix.Vector, contentNode) bool) bool { for _, nn := range n { if !nn.forEeachContentNode(f) { return false } } return true } type contentNodes []contentNode func (n contentNodes) Path() string { return n.sample().Path() } func (m contentNodes) isEmpty() bool { return len(m) == 0 } func (n contentNodes) forEeachContentNode(f func(v sitesmatrix.Vector, n contentNode) bool) bool { for _, nn := range n { if !nn.forEeachContentNode(f) { return false } } return true } func (n contentNodes) lookupContentNode(v sitesmatrix.Vector) contentNode { for _, nn := range n { if vv := nn.(contentNodeLookupContentNode).lookupContentNode(v); vv != nil { return vv } } return nil } func (m *contentNodes) nodeCategoryPage() { // Marker method. } func (n contentNodes) sample() contentNode { if len(n) == 0 { panic("pageMetaSourcesSlice is empty") } return n[0] } type contentNodesMap map[sitesmatrix.Vector]contentNode var cnh helperContentNode type helperContentNode struct{} func (h helperContentNode) findContentNodeForSiteVector(q sitesmatrix.Vector, fallback bool, candidates contentNodeSeq) contentNodeForSite { var ( best contentNodeForSite = nil bestDistance int bestWeight int ) for n := range candidates { // The order of candidates is unstable, so we need to compare the matches to // get stable output. This compare will also make sure that we pick // language, version and role according to their individual sort order: // Closer is better, and matches above are better than matches below. if m := n.(contentNodeLookupContentNodes).lookupContentNodes(q, fallback); m != nil { for nn := range m { vec := nn.siteVector() var w int if wp, ok := nn.(contentNodeContentWeightProvider); ok { w = wp.contentWeight() } if q == vec && w > 0 { // Exact match backed by a file. return nn } distance := q.Distance(vec) distanceAbs := absint(distance) if best == nil { best = nn bestDistance = distance bestWeight = w } else { bestDistanceAbs := absint(bestDistance) if w > 0 && bestWeight == 0 { // Prefer file-backed pages over auto pages. best = nn bestDistance = distance bestWeight = w } else if (w > 0) == (bestWeight > 0) { // Both file-backed or both auto: use distance. if distanceAbs < bestDistanceAbs { best = nn bestDistance = distance bestWeight = w } else if distanceAbs == bestDistanceAbs && distance > 0 { best = nn bestDistance = distance bestWeight = w } } } } } } return best } func (h helperContentNode) markStale(n contentNode) { n.forEeachContentNode( func(_ sitesmatrix.Vector, nn contentNode) bool { resource.MarkStale(nn) return true }, ) } func (h helperContentNode) resetBuildState(n contentNode) { n.forEeachContentNode( func(_ sitesmatrix.Vector, nn contentNode) bool { if nnn, ok := nn.(contentNodeBuildStateResetter); ok { nnn.resetBuildState() } return true }, ) } func (h helperContentNode) isBranchNode(n contentNode) bool { switch nn := n.(type) { case *pageMetaSource: return nn.pathInfo.IsBranchBundle() case *pageState: return nn.IsBranch() case contentNodeSampleProvider: return h.isBranchNode(nn.sample()) default: return false } } func (h helperContentNode) isPageNode(n contentNode) bool { n, _ = h.one(n) switch n.(type) { case contentNodePage: return true default: return false } } func (helperContentNode) one(n contentNode) (nn contentNode, hasMore bool) { n.forEeachContentNode(func(_ sitesmatrix.Vector, n contentNode) bool { if nn == nil { nn = n return true } else { hasMore = true return false } }) return } func (h helperContentNode) GetIdentity(n contentNode) identity.Identity { switch nn := n.(type) { case identity.IdentityProvider: return nn.GetIdentity() case contentNodeSampleProvider: return h.GetIdentity(nn.sample()) default: return identity.Anonymous } } func (h helperContentNode) PathInfo(n contentNode) *paths.Path { switch nn := n.(type) { case interface{ PathInfo() *paths.Path }: return nn.PathInfo() case contentNodeSampleProvider: return h.PathInfo(nn.sample()) default: return nil } } func (h helperContentNode) toForEachIdentityProvider(n contentNode) identity.ForEeachIdentityProvider { return identity.ForEeachIdentityProviderFunc( func(cb func(id identity.Identity) bool) bool { return n.forEeachContentNode( func(vec sitesmatrix.Vector, nn contentNode) bool { return cb(h.GetIdentity(n)) }, ) }) } type weightedContentNode struct { n contentNode weight int term *pageWithOrdinal } func (n contentNodesMap) isEmpty() bool { return len(n) == 0 } func absint(i int) int { if i < 0 { return -i } return i } func contentNodeToContentNodesPage(n contentNode) (contentNodesMap, bool) { switch v := n.(type) { case contentNodesMap: return v, false case *pageState: return contentNodesMap{v.s.siteVector: v}, true default: panic(fmt.Sprintf("contentNodeToContentNodesPage: unexpected type %T", n)) } } // hasAutoContentNode reports whether n holds at least one node that is not backed by a file. func (h helperContentNode) hasAutoContentNode(n contentNodeForEach) bool { return !n.forEeachContentNode(func(_ sitesmatrix.Vector, nn contentNode) bool { wp, ok := nn.(contentNodeContentWeightProvider) return ok && wp.contentWeight() > 0 }) } func (h helperContentNode) contentNodeToSeq(n contentNodeForEach) contentNodeSeq { if nn, ok := n.(contentNodeSeq); ok { return nn } return func(yield func(contentNode) bool) { n.forEeachContentNode(func(_ sitesmatrix.Vector, nn contentNode) bool { return yield(nn) }) } } func (h helperContentNode) contentNodeToSeq2(n contentNodeForEach) contentNodeSeq2 { if nn, ok := n.(contentNodeSeq2); ok { return nn } return func(yield func(sitesmatrix.Vector, contentNode) bool) { n.forEeachContentNode(func(vec sitesmatrix.Vector, nn contentNode) bool { return yield(vec, nn) }) } } func (ps contentNodesMap) forEeachContentNode(f func(v sitesmatrix.Vector, n contentNode) bool) bool { for vec, nn := range ps { if !f(vec, nn) { return false } } return true } func (n contentNodesMap) lookupContentNode(v sitesmatrix.Vector) contentNode { if vv, ok := n[v]; ok { return vv } return nil } func (n contentNodesMap) sample() contentNode { for _, nn := range n { return nn } return nil } func (n contentNodesMap) siteVectors() sitesmatrix.VectorIterator { return n } func (n contentNodesMap) ForEachVector(yield func(v sitesmatrix.Vector) bool) bool { for v := range n { if !yield(v) { return false } } return true } func (n contentNodesMap) LenVectors() int { return len(n) } func (n contentNodesMap) VectorSample() sitesmatrix.Vector { for v := range n { return v } panic("no vectors") }