pages_process.go (4810B)
1 // Copyright 2019 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 hugolib
15
16 import (
17 "context"
18 "fmt"
19 "path/filepath"
20
21 "github.com/gohugoio/hugo/config"
22 "github.com/gohugoio/hugo/source"
23
24 "github.com/gohugoio/hugo/hugofs/files"
25 "golang.org/x/sync/errgroup"
26
27 "github.com/gohugoio/hugo/common/herrors"
28 "github.com/gohugoio/hugo/hugofs"
29 )
30
31 func newPagesProcessor(h *HugoSites, sp *source.SourceSpec) *pagesProcessor {
32 procs := make(map[string]pagesCollectorProcessorProvider)
33 for _, s := range h.Sites {
34 procs[s.Lang()] = &sitePagesProcessor{
35 m: s.pageMap,
36 errorSender: s.h,
37 itemChan: make(chan interface{}, config.GetNumWorkerMultiplier()*2),
38 renderStaticToDisk: h.Cfg.GetBool("renderStaticToDisk"),
39 }
40 }
41 return &pagesProcessor{
42 procs: procs,
43 }
44 }
45
46 type pagesCollectorProcessorProvider interface {
47 Process(item any) error
48 Start(ctx context.Context) context.Context
49 Wait() error
50 }
51
52 type pagesProcessor struct {
53 // Per language/Site
54 procs map[string]pagesCollectorProcessorProvider
55 }
56
57 func (proc *pagesProcessor) Process(item any) error {
58 switch v := item.(type) {
59 // Page bundles mapped to their language.
60 case pageBundles:
61 for _, vv := range v {
62 proc.getProcFromFi(vv.header).Process(vv)
63 }
64 case hugofs.FileMetaInfo:
65 proc.getProcFromFi(v).Process(v)
66 default:
67 panic(fmt.Sprintf("unrecognized item type in Process: %T", item))
68
69 }
70
71 return nil
72 }
73
74 func (proc *pagesProcessor) Start(ctx context.Context) context.Context {
75 for _, p := range proc.procs {
76 ctx = p.Start(ctx)
77 }
78 return ctx
79 }
80
81 func (proc *pagesProcessor) Wait() error {
82 var err error
83 for _, p := range proc.procs {
84 if e := p.Wait(); e != nil {
85 err = e
86 }
87 }
88 return err
89 }
90
91 func (proc *pagesProcessor) getProcFromFi(fi hugofs.FileMetaInfo) pagesCollectorProcessorProvider {
92 if p, found := proc.procs[fi.Meta().Lang]; found {
93 return p
94 }
95 return defaultPageProcessor
96 }
97
98 type nopPageProcessor int
99
100 func (nopPageProcessor) Process(item any) error {
101 return nil
102 }
103
104 func (nopPageProcessor) Start(ctx context.Context) context.Context {
105 return context.Background()
106 }
107
108 func (nopPageProcessor) Wait() error {
109 return nil
110 }
111
112 var defaultPageProcessor = new(nopPageProcessor)
113
114 type sitePagesProcessor struct {
115 m *pageMap
116 errorSender herrors.ErrorSender
117
118 ctx context.Context
119 itemChan chan any
120 itemGroup *errgroup.Group
121
122 renderStaticToDisk bool
123 }
124
125 func (p *sitePagesProcessor) Process(item any) error {
126 select {
127 case <-p.ctx.Done():
128 return nil
129 default:
130 p.itemChan <- item
131 }
132 return nil
133 }
134
135 func (p *sitePagesProcessor) Start(ctx context.Context) context.Context {
136 p.itemGroup, ctx = errgroup.WithContext(ctx)
137 p.ctx = ctx
138 p.itemGroup.Go(func() error {
139 for item := range p.itemChan {
140 if err := p.doProcess(item); err != nil {
141 return err
142 }
143 }
144 return nil
145 })
146 return ctx
147 }
148
149 func (p *sitePagesProcessor) Wait() error {
150 close(p.itemChan)
151 return p.itemGroup.Wait()
152 }
153
154 func (p *sitePagesProcessor) copyFile(fim hugofs.FileMetaInfo) error {
155 meta := fim.Meta()
156 f, err := meta.Open()
157 if err != nil {
158 return fmt.Errorf("copyFile: failed to open: %w", err)
159 }
160
161 s := p.m.s
162
163 target := filepath.Join(s.PathSpec.GetTargetLanguageBasePath(), meta.Path)
164
165 defer f.Close()
166
167 fs := s.PublishFs
168 if p.renderStaticToDisk {
169 fs = s.PublishFsStatic
170 }
171
172 return s.publish(&s.PathSpec.ProcessingStats.Files, target, f, fs)
173 }
174
175 func (p *sitePagesProcessor) doProcess(item any) error {
176 m := p.m
177 switch v := item.(type) {
178 case *fileinfoBundle:
179 if err := m.AddFilesBundle(v.header, v.resources...); err != nil {
180 return err
181 }
182 case hugofs.FileMetaInfo:
183 if p.shouldSkip(v) {
184 return nil
185 }
186 meta := v.Meta()
187
188 classifier := meta.Classifier
189 switch classifier {
190 case files.ContentClassContent:
191 if err := m.AddFilesBundle(v); err != nil {
192 return err
193 }
194 case files.ContentClassFile:
195 if err := p.copyFile(v); err != nil {
196 return err
197 }
198 default:
199 panic(fmt.Sprintf("invalid classifier: %q", classifier))
200 }
201 default:
202 panic(fmt.Sprintf("unrecognized item type in Process: %T", item))
203 }
204 return nil
205 }
206
207 func (p *sitePagesProcessor) shouldSkip(fim hugofs.FileMetaInfo) bool {
208 // TODO(ep) unify
209 return p.m.s.SourceSpec.DisabledLanguages[fim.Meta().Lang]
210 }