goldsmith/context.go
Alex Yatskov e408fa9335 Squashed commit of the following:
commit 106991bfd59bb95977045399f7b4e5f4e7addc56
Author: Alex Yatskov <alex@foosoft.net>
Date:   Sat Jan 8 11:19:21 2022 -0800

    Rename property functions

commit 07f6033d4e86df257806af16de002ce8fad3d67f
Author: Alex Yatskov <alex@foosoft.net>
Date:   Sat Jan 8 11:16:46 2022 -0800

    Update rewrite

commit ea2dc0b4d223d54832752f0efd3eb46d81d17b50
Author: Alex Yatskov <alex@foosoft.net>
Date:   Fri Jan 7 22:47:17 2022 -0800

    Add more property methods

commit e2f7f8dc7ebc5b668539e8233e323a7c256eced2
Author: Alex Yatskov <alex@foosoft.net>
Date:   Fri Jan 7 22:39:23 2022 -0800

    Add step indexing

commit a7ed2a0b1c95ed9fbb0bbd87c779cf51a92f0b5f
Author: Alex Yatskov <alex@foosoft.net>
Date:   Fri Jan 7 20:50:19 2022 -0800

    Use self

commit 7ecc01e508c06680dcb6b6ecd4265a7f72e2c933
Author: Alex Yatskov <alex@foosoft.net>
Date:   Sun Aug 22 12:33:09 2021 -0700

    Cleanup

commit 87dd28a4c14b88bea061ca913f68e272cca787f9
Author: Alex Yatskov <alex@foosoft.net>
Date:   Sun Aug 22 12:09:41 2021 -0700

    Cleanup

commit 129130128d2a50f119c46f70e7cb70ef421892ff
Merge: 629fce0 e751d70
Author: Alex Yatskov <alex@foosoft.net>
Date:   Sat Aug 21 12:18:09 2021 -0700

    Merge branch 'master' into dev

commit 629fce06a8fca6810ec772f558d9ffabda016d01
Author: Alex Yatskov <alex@foosoft.net>
Date:   Sun Jun 27 19:34:53 2021 -0700

    Abstract metadata
2022-01-08 18:08:36 -08:00

162 lines
3.9 KiB
Go

package goldsmith
import (
"bytes"
"errors"
"io"
"io/ioutil"
"os"
"runtime"
"sync"
"time"
)
// Context corresponds to the current link in the chain and provides methods
// that enable plugins to inject new files into the chain.
type Context struct {
goldsmith *Goldsmith
plugin Plugin
filtersExt filterStack
filtersInt filterStack
threads int
index int
filesIn chan *File
filesOut chan *File
}
// CreateFileFrom data creates a new file instance from the provided data buffer.
func (self *Context) CreateFileFromReader(sourcePath string, reader io.Reader) (*File, error) {
data, err := ioutil.ReadAll(reader)
if err != nil {
return nil, err
}
file := &File{
relPath: sourcePath,
props: make(map[string]Prop),
modTime: time.Now(),
size: int64(len(data)),
reader: bytes.NewReader(data),
index: self.index,
}
return file, nil
}
// CreateFileFromAsset creates a new file instance from the provided file path.
func (self *Context) CreateFileFromAsset(sourcePath, dataPath string) (*File, error) {
info, err := os.Stat(dataPath)
if err != nil {
return nil, err
}
if info.IsDir() {
return nil, errors.New("assets must be files")
}
file := &File{
relPath: sourcePath,
props: make(map[string]Prop),
modTime: info.ModTime(),
size: info.Size(),
dataPath: dataPath,
index: self.index,
}
return file, nil
}
// DispatchFile causes the file to get passed to the next link in the chain.
func (self *Context) DispatchFile(file *File) {
self.filesOut <- file
}
// DispatchAndCacheFile caches the file data (excluding the metadata), taking
// dependencies on any input files that are needed to generate it, and then
// passes it to the next link in the chain.
func (self *Context) DispatchAndCacheFile(outputFile *File, inputFiles ...*File) {
if self.goldsmith.cache != nil {
self.goldsmith.cache.storeFile(self, outputFile, inputFiles)
}
self.filesOut <- outputFile
}
// RetrieveCachedFile looks up file data (excluding the metadata), given an
// output path and any input files that are needed to generate it. The function
// will return nil if the desired file is not found in the cache.
func (self *Context) RetrieveCachedFile(outputPath string, inputFiles ...*File) *File {
var outputFile *File
if self.goldsmith.cache != nil {
outputFile, _ = self.goldsmith.cache.retrieveFile(self, outputPath, inputFiles)
}
return outputFile
}
// Specify internal filter(s) that exclude files from being processed.
func (self *Context) Filter(filters ...Filter) *Context {
for _, filter := range filters {
self.filtersInt.push(filter, self.index)
}
return self
}
// Specify the maximum number of threads used for processing.
func (self *Context) Threads(threads int) *Context {
self.threads = threads
return self
}
func (self *Context) step() {
defer close(self.filesOut)
if initializer, ok := self.plugin.(Initializer); ok {
if err := initializer.Initialize(self); err != nil {
self.goldsmith.fault(self.plugin.Name(), nil, err)
return
}
}
if self.filesIn != nil {
processor, _ := self.plugin.(Processor)
threads := self.threads
if threads < 1 {
threads = runtime.NumCPU()
}
var wg sync.WaitGroup
for i := 0; i < threads; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for inputFile := range self.filesIn {
if processor != nil && self.filtersInt.accept(inputFile) && self.filtersExt.accept(inputFile) {
if _, err := inputFile.Seek(0, os.SEEK_SET); err != nil {
self.goldsmith.fault("core", inputFile, err)
}
if err := processor.Process(self, inputFile); err != nil {
self.goldsmith.fault(self.plugin.Name(), inputFile, err)
}
} else {
self.filesOut <- inputFile
}
}
}()
}
wg.Wait()
}
if finalizer, ok := self.plugin.(Finalizer); ok {
if err := finalizer.Finalize(self); err != nil {
self.goldsmith.fault(self.plugin.Name(), nil, err)
}
}
}