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