github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/hugolib/pages_process.go (about)

     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  	"github.com/pkg/errors"
    26  	"golang.org/x/sync/errgroup"
    27  
    28  	"github.com/gohugoio/hugo/common/herrors"
    29  	"github.com/gohugoio/hugo/hugofs"
    30  )
    31  
    32  func newPagesProcessor(h *HugoSites, sp *source.SourceSpec) *pagesProcessor {
    33  	procs := make(map[string]pagesCollectorProcessorProvider)
    34  	for _, s := range h.Sites {
    35  		procs[s.Lang()] = &sitePagesProcessor{
    36  			m:           s.pageMap,
    37  			errorSender: s.h,
    38  			itemChan:    make(chan interface{}, config.GetNumWorkerMultiplier()*2),
    39  		}
    40  	}
    41  	return &pagesProcessor{
    42  		procs: procs,
    43  	}
    44  }
    45  
    46  type pagesCollectorProcessorProvider interface {
    47  	Process(item interface{}) 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 interface{}) 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 interface{}) 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 interface{}
   120  	itemGroup *errgroup.Group
   121  }
   122  
   123  func (p *sitePagesProcessor) Process(item interface{}) error {
   124  	select {
   125  	case <-p.ctx.Done():
   126  		return nil
   127  	default:
   128  		p.itemChan <- item
   129  	}
   130  	return nil
   131  }
   132  
   133  func (p *sitePagesProcessor) Start(ctx context.Context) context.Context {
   134  	p.itemGroup, ctx = errgroup.WithContext(ctx)
   135  	p.ctx = ctx
   136  	p.itemGroup.Go(func() error {
   137  		for item := range p.itemChan {
   138  			if err := p.doProcess(item); err != nil {
   139  				return err
   140  			}
   141  		}
   142  		return nil
   143  	})
   144  	return ctx
   145  }
   146  
   147  func (p *sitePagesProcessor) Wait() error {
   148  	close(p.itemChan)
   149  	return p.itemGroup.Wait()
   150  }
   151  
   152  func (p *sitePagesProcessor) copyFile(fim hugofs.FileMetaInfo) error {
   153  	meta := fim.Meta()
   154  	f, err := meta.Open()
   155  	if err != nil {
   156  		return errors.Wrap(err, "copyFile: failed to open")
   157  	}
   158  
   159  	s := p.m.s
   160  
   161  	target := filepath.Join(s.PathSpec.GetTargetLanguageBasePath(), meta.Path)
   162  
   163  	defer f.Close()
   164  
   165  	return s.publish(&s.PathSpec.ProcessingStats.Files, target, f)
   166  }
   167  
   168  func (p *sitePagesProcessor) doProcess(item interface{}) error {
   169  	m := p.m
   170  	switch v := item.(type) {
   171  	case *fileinfoBundle:
   172  		if err := m.AddFilesBundle(v.header, v.resources...); err != nil {
   173  			return err
   174  		}
   175  	case hugofs.FileMetaInfo:
   176  		if p.shouldSkip(v) {
   177  			return nil
   178  		}
   179  		meta := v.Meta()
   180  
   181  		classifier := meta.Classifier
   182  		switch classifier {
   183  		case files.ContentClassContent:
   184  			if err := m.AddFilesBundle(v); err != nil {
   185  				return err
   186  			}
   187  		case files.ContentClassFile:
   188  			if err := p.copyFile(v); err != nil {
   189  				return err
   190  			}
   191  		default:
   192  			panic(fmt.Sprintf("invalid classifier: %q", classifier))
   193  		}
   194  	default:
   195  		panic(fmt.Sprintf("unrecognized item type in Process: %T", item))
   196  	}
   197  	return nil
   198  }
   199  
   200  func (p *sitePagesProcessor) shouldSkip(fim hugofs.FileMetaInfo) bool {
   201  	// TODO(ep) unify
   202  	return p.m.s.SourceSpec.DisabledLanguages[fim.Meta().Lang]
   203  }