~foosoft/goldsmith

9ba1c7cfeb909ebf99b7f938c044f45f700fbb65 — Alex Yatskov 1 year, 10 days ago 05f7ee5
Revert "Make Context be an interface"

This reverts commit ab0c1c53d26abb1605771cefa7df0366b183a1f1.
M cache.go => cache.go +3 -3
@@ 15,7 15,7 @@ type cache struct {
	baseDir string
}

func (self *cache) retrieveFile(context *contextImpl, outputPath string, inputFiles []File) (File, error) {
func (self *cache) retrieveFile(context *Context, outputPath string, inputFiles []File) (File, error) {
	cachePath, err := self.buildCachePath(context, outputPath, inputFiles)
	if err != nil {
		return nil, err


@@ 33,7 33,7 @@ func (self *cache) retrieveFile(context *contextImpl, outputPath string, inputFi
	return outputFile, nil
}

func (self *cache) storeFile(context *contextImpl, outputFile File, inputFiles []File) error {
func (self *cache) storeFile(context *Context, outputFile File, inputFiles []File) error {
	cachePath, err := self.buildCachePath(context, outputFile.Path(), inputFiles)
	if err != nil {
		return err


@@ 69,7 69,7 @@ func (self *cache) storeFile(context *contextImpl, outputFile File, inputFiles [
	return nil
}

func (self *cache) buildCachePath(context *contextImpl, outputPath string, inputFiles []File) (string, error) {
func (self *cache) buildCachePath(context *Context, outputPath string, inputFiles []File) (string, error) {
	hasher := crc32.NewIEEE()
	hasher.Write([]byte(outputPath))


M context.go => context.go +9 -36
@@ 13,34 13,7 @@ import (

// Context corresponds to the current link in the chain and provides methods
// that enable plugins to inject new files into the chain.
type Context interface {
	// CreateFileFrom data creates a new file instance from the provided data buffer.
	CreateFileFromReader(sourcePath string, reader io.Reader) (File, error)

	// CreateFileFromAsset creates a new file instance from the provided file path.
	CreateFileFromAsset(sourcePath, dataPath string) (File, error)

	// DispatchFile causes the file to get passed to the next link in the chain.
	DispatchFile(file 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.
	DispatchAndCacheFile(outputFile File, inputFiles ...File)

	// 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.
	RetrieveCachedFile(outputPath string, inputFiles ...File) File

	// Specify internal filter(s) that exclude files from being processed.
	Filter(filters ...Filter) Context

	// Specify the maximum number of threads used for processing.
	Threads(threads int) Context
}

type contextImpl struct {
type Context struct {
	goldsmith *Goldsmith

	plugin Plugin


@@ 56,7 29,7 @@ type contextImpl struct {
}

// CreateFileFrom data creates a new file instance from the provided data buffer.
func (self *contextImpl) CreateFileFromReader(sourcePath string, reader io.Reader) (File, error) {
func (self *Context) CreateFileFromReader(sourcePath string, reader io.Reader) (File, error) {
	data, err := io.ReadAll(reader)
	if err != nil {
		return nil, err


@@ 77,7 50,7 @@ func (self *contextImpl) CreateFileFromReader(sourcePath string, reader io.Reade
}

// CreateFileFromAsset creates a new file instance from the provided file path.
func (self *contextImpl) CreateFileFromAsset(sourcePath, dataPath string) (File, error) {
func (self *Context) CreateFileFromAsset(sourcePath, dataPath string) (File, error) {
	if filepath.IsAbs(sourcePath) {
		return nil, errors.New("source paths must be relative")
	}


@@ 109,14 82,14 @@ func (self *contextImpl) CreateFileFromAsset(sourcePath, dataPath string) (File,
}

// DispatchFile causes the file to get passed to the next link in the chain.
func (self *contextImpl) DispatchFile(file File) {
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 *contextImpl) DispatchAndCacheFile(outputFile File, inputFiles ...File) {
func (self *Context) DispatchAndCacheFile(outputFile File, inputFiles ...File) {
	if self.goldsmith.state.cache != nil {
		self.goldsmith.state.cache.storeFile(self, outputFile, inputFiles)
	}


@@ 127,7 100,7 @@ func (self *contextImpl) DispatchAndCacheFile(outputFile File, inputFiles ...Fil
// 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 *contextImpl) RetrieveCachedFile(outputPath string, inputFiles ...File) File {
func (self *Context) RetrieveCachedFile(outputPath string, inputFiles ...File) File {
	var outputFile File
	if self.goldsmith.state.cache != nil {
		outputFile, _ = self.goldsmith.state.cache.retrieveFile(self, outputPath, inputFiles)


@@ 137,7 110,7 @@ func (self *contextImpl) RetrieveCachedFile(outputPath string, inputFiles ...Fil
}

// Specify internal filter(s) that exclude files from being processed.
func (self *contextImpl) Filter(filters ...Filter) Context {
func (self *Context) Filter(filters ...Filter) *Context {
	for _, filter := range filters {
		self.filtersInt.push(filter, self.index)
	}


@@ 146,12 119,12 @@ func (self *contextImpl) Filter(filters ...Filter) Context {
}

// Specify the maximum number of threads used for processing.
func (self *contextImpl) Threads(threads int) Context {
func (self *Context) Threads(threads int) *Context {
	self.threads = threads
	return self
}

func (self *contextImpl) step() {
func (self *Context) step() {
	defer close(self.filesOut)

	if initializer, ok := self.plugin.(Initializer); ok {

M extension.go => extension.go +3 -3
@@ 9,18 9,18 @@ type Plugin interface {
// Initializer is used to optionally initialize a plugin and to specify a
// filter to be used for determining which files will be processed.
type Initializer interface {
	Initialize(context Context) error
	Initialize(context *Context) error
}

// Processor allows for optional processing of files passing through a plugin.
type Processor interface {
	Process(context Context, file File) error
	Process(context *Context, file File) error
}

// Finalizer allows for optional finalization of a plugin after all files
// queued in the chain have passed through it.
type Finalizer interface {
	Finalize(context Context) error
	Finalize(context *Context) error
}

// Filter is used to determine which files should continue in the chain.

M file_exporter.go => file_exporter.go +3 -3
@@ 16,13 16,13 @@ func (*fileExporter) Name() string {
	return "exporter"
}

func (self *fileExporter) Initialize(context Context) error {
func (self *fileExporter) Initialize(context *Context) error {
	self.tokens = make(map[string]bool)
	context.Threads(1)
	return nil
}

func (self *fileExporter) Process(context Context, file File) error {
func (self *fileExporter) Process(context *Context, file File) error {
	slicePath := func(path string) string {
		if filepath.IsAbs(path) {
			var err error


@@ 64,7 64,7 @@ func (self *fileExporter) Process(context Context, file File) error {
	return nil
}

func (self *fileExporter) Finalize(context Context) error {
func (self *fileExporter) Finalize(context *Context) error {
	if !self.clean {
		return nil
	}

M file_importer.go => file_importer.go +1 -1
@@ 13,7 13,7 @@ func (*fileImporter) Name() string {
	return "importer"
}

func (self *fileImporter) Initialize(context Context) error {
func (self *fileImporter) Initialize(context *Context) error {
	return filepath.Walk(self.sourceDir, func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil

M goldsmith.go => goldsmith.go +2 -2
@@ 7,7 7,7 @@ import (
)

type chainState struct {
	contexts []*contextImpl
	contexts []*Context

	cache   *cache
	filters filterStack


@@ 44,7 44,7 @@ func (self *Goldsmith) Clean(clean bool) *Goldsmith {

// Chain links a plugin instance into the chain.
func (self *Goldsmith) Chain(plugin Plugin) *Goldsmith {
	context := &contextImpl{
	context := &Context{
		goldsmith:  self,
		plugin:     plugin,
		filtersExt: append(filterStack(nil), self.state.filters...),

M plugins/absolute/absolute.go => plugins/absolute/absolute.go +2 -2
@@ 45,12 45,12 @@ func (*Absolute) Name() string {
	return "absolute"
}

func (*Absolute) Initialize(context goldsmith.Context) error {
func (*Absolute) Initialize(context *goldsmith.Context) error {
	context.Filter(wildcard.New("**/*.html", "**/*.htm"))
	return nil
}

func (self *Absolute) Process(context goldsmith.Context, inputFile goldsmith.File) error {
func (self *Absolute) Process(context *goldsmith.Context, inputFile goldsmith.File) error {
	if outputFile := context.RetrieveCachedFile(inputFile.Path(), inputFile); outputFile != nil {
		outputFile.CopyProps(inputFile)
		context.DispatchFile(outputFile)

M plugins/breadcrumbs/breadcrumbs.go => plugins/breadcrumbs/breadcrumbs.go +3 -3
@@ 70,12 70,12 @@ func (*Breadcrumbs) Name() string {
	return "breadcrumbs"
}

func (*Breadcrumbs) Initialize(context goldsmith.Context) error {
func (*Breadcrumbs) Initialize(context *goldsmith.Context) error {
	context.Filter(wildcard.New("**/*.html", "**/*.htm"))
	return nil
}

func (self *Breadcrumbs) Process(context goldsmith.Context, inputFile goldsmith.File) error {
func (self *Breadcrumbs) Process(context *goldsmith.Context, inputFile goldsmith.File) error {
	var parentNameStr string
	if parentName, ok := inputFile.Prop(self.parentKey); ok {
		parentNameStr, _ = parentName.(string)


@@ 103,7 103,7 @@ func (self *Breadcrumbs) Process(context goldsmith.Context, inputFile goldsmith.
	return nil
}

func (self *Breadcrumbs) Finalize(context goldsmith.Context) error {
func (self *Breadcrumbs) Finalize(context *goldsmith.Context) error {
	for _, node := range self.allNodes {
		if len(node.parentName) == 0 {
			continue

M plugins/collection/collection.go => plugins/collection/collection.go +3 -3
@@ 61,12 61,12 @@ func (*Collection) Name() string {
	return "collection"
}

func (*Collection) Initialize(context goldsmith.Context) error {
func (*Collection) Initialize(context *goldsmith.Context) error {
	context.Filter(wildcard.New("**/*.html", "**/*.htm"))
	return nil
}

func (self *Collection) Process(context goldsmith.Context, inputFile goldsmith.File) error {
func (self *Collection) Process(context *goldsmith.Context, inputFile goldsmith.File) error {
	self.mutex.Lock()
	defer func() {
		inputFile.SetProp(self.groupsKey, self.groups)


@@ 96,7 96,7 @@ func (self *Collection) Process(context goldsmith.Context, inputFile goldsmith.F
	return nil
}

func (self *Collection) Finalize(context goldsmith.Context) error {
func (self *Collection) Finalize(context *goldsmith.Context) error {
	for _, files := range self.groups {
		fg := &fileSorter{files, self.comparer}
		sort.Sort(fg)

M plugins/document/document.go => plugins/document/document.go +3 -3
@@ 32,12 32,12 @@ func (*Document) Name() string {
	return "document"
}

func (*Document) Initialize(context goldsmith.Context) error {
func (*Document) Initialize(context *goldsmith.Context) error {
	context.Filter(wildcard.New("**/*.html", "**/*.htm"))
	return nil
}

func (self *Document) Process(context goldsmith.Context, inputFile goldsmith.File) error {
func (self *Document) Process(context *goldsmith.Context, inputFile goldsmith.File) error {
	doc, err := goquery.NewDocumentFromReader(inputFile)
	if err != nil {
		return err


@@ 61,7 61,7 @@ func (self *Document) Process(context goldsmith.Context, inputFile goldsmith.Fil
	return nil
}

func (self *Document) Finalize(context goldsmith.Context) error {
func (self *Document) Finalize(context *goldsmith.Context) error {
	for _, file := range self.files {
		context.DispatchFile(file)
	}

M plugins/forward/forward.go => plugins/forward/forward.go +1 -1
@@ 53,7 53,7 @@ func (*Forward) Name() string {
	return "forward"
}

func (self *Forward) Initialize(context goldsmith.Context) error {
func (self *Forward) Initialize(context *goldsmith.Context) error {
	for sourcePath, targetPath := range self.pathMap {
		sourceFile, err := context.CreateFileFromReader(sourcePath, bytes.NewReader(nil))
		if err != nil {

M plugins/frontmatter/frontmatter.go => plugins/frontmatter/frontmatter.go +2 -2
@@ 66,12 66,12 @@ func (*FrontMatter) Name() string {
	return "frontmatter"
}

func (*FrontMatter) Initialize(context goldsmith.Context) error {
func (*FrontMatter) Initialize(context *goldsmith.Context) error {
	context.Filter(wildcard.New("**/*.md", "**/*.markdown", "**/*.rst", "**/*.txt", "**/*.html", "**/*.htm"))
	return nil
}

func (*FrontMatter) Process(context goldsmith.Context, inputFile goldsmith.File) error {
func (*FrontMatter) Process(context *goldsmith.Context, inputFile goldsmith.File) error {
	meta, body, err := parse(inputFile)
	if err != nil {
		return err

M plugins/index/index.go => plugins/index/index.go +2 -2
@@ 60,7 60,7 @@ func (*Index) Name() string {
	return "index"
}

func (self *Index) Process(context goldsmith.Context, inputFile goldsmith.File) error {
func (self *Index) Process(context *goldsmith.Context, inputFile goldsmith.File) error {
	self.mutex.Lock()
	defer self.mutex.Unlock()



@@ 105,7 105,7 @@ func (self *Index) Process(context goldsmith.Context, inputFile goldsmith.File) 
	return nil
}

func (self *Index) Finalize(context goldsmith.Context) error {
func (self *Index) Finalize(context *goldsmith.Context) error {
	for name, list := range self.dirLists {
		sort.Sort(list.entries)


M plugins/layout/layout.go => plugins/layout/layout.go +3 -3
@@ 64,13 64,13 @@ func (*Layout) Name() string {
	return "layout"
}

func (self *Layout) Initialize(context goldsmith.Context) error {
func (self *Layout) Initialize(context *goldsmith.Context) error {
	self.template = template.New("").Funcs(self.helpers)
	context.Filter(wildcard.New("**/*.html", "**/*.htm", "**/*.tmpl", "**/*.gohtml"))
	return nil
}

func (self *Layout) Process(context goldsmith.Context, inputFile goldsmith.File) error {
func (self *Layout) Process(context *goldsmith.Context, inputFile goldsmith.File) error {
	self.mutex.Lock()
	defer self.mutex.Unlock()



@@ 94,7 94,7 @@ func (self *Layout) Process(context goldsmith.Context, inputFile goldsmith.File)
	return nil
}

func (self *Layout) Finalize(context goldsmith.Context) error {
func (self *Layout) Finalize(context *goldsmith.Context) error {
	for _, templateFile := range self.templateFiles {
		var buff bytes.Buffer
		if _, err := templateFile.WriteTo(&buff); err != nil {

M plugins/livejs/livejs.go => plugins/livejs/livejs.go +2 -2
@@ 31,13 31,13 @@ func (*LiveJs) Name() string {
	return "livejs"
}

func (self *LiveJs) Initialize(context goldsmith.Context) error {
func (self *LiveJs) Initialize(context *goldsmith.Context) error {
	self.html = fmt.Sprintf("\n<!-- begin livejs code -->\n<script>\n%s\n</script>\n<!-- end livejs code -->\n", livejs)
	context.Filter(wildcard.New("**/*.html", "**/*.htm"))
	return nil
}

func (self *LiveJs) Process(context goldsmith.Context, inputFile goldsmith.File) error {
func (self *LiveJs) Process(context *goldsmith.Context, inputFile goldsmith.File) error {
	if outputFile := context.RetrieveCachedFile(inputFile.Path(), inputFile); outputFile != nil {
		outputFile.CopyProps(inputFile)
		context.DispatchFile(outputFile)

M plugins/markdown/markdown.go => plugins/markdown/markdown.go +2 -2
@@ 42,12 42,12 @@ func (*Markdown) Name() string {
	return "markdown"
}

func (self *Markdown) Initialize(context goldsmith.Context) error {
func (self *Markdown) Initialize(context *goldsmith.Context) error {
	context.Filter(wildcard.New("**/*.md", "**/*.markdown"))
	return nil
}

func (self *Markdown) Process(context goldsmith.Context, inputFile goldsmith.File) error {
func (self *Markdown) Process(context *goldsmith.Context, inputFile goldsmith.File) error {
	outputPath := strings.TrimSuffix(inputFile.Path(), path.Ext(inputFile.Path())) + ".html"
	if outputFile := context.RetrieveCachedFile(outputPath, inputFile); outputFile != nil {
		outputFile.CopyProps(inputFile)

M plugins/minify/minify.go => plugins/minify/minify.go +2 -2
@@ 32,12 32,12 @@ func (*Minify) Name() string {
	return "minify"
}

func (*Minify) Initialize(context goldsmith.Context) error {
func (*Minify) Initialize(context *goldsmith.Context) error {
	context.Filter(wildcard.New("**/*.css", "**/*.html", "**/*.htm", "**/*.js", "**/*.svg", "**/*.json", "**/*.xml"))
	return nil
}

func (*Minify) Process(context goldsmith.Context, inputFile goldsmith.File) error {
func (*Minify) Process(context *goldsmith.Context, inputFile goldsmith.File) error {
	if outputFile := context.RetrieveCachedFile(inputFile.Path(), inputFile); outputFile != nil {
		outputFile.CopyProps(inputFile)
		context.DispatchFile(outputFile)

M plugins/pager/pager.go => plugins/pager/pager.go +3 -3
@@ 109,12 109,12 @@ func (*Pager) Name() string {
	return "pager"
}

func (*Pager) Initialize(context goldsmith.Context) error {
func (*Pager) Initialize(context *goldsmith.Context) error {
	context.Filter(wildcard.New("**/*.html", "**/*.htm"))
	return nil
}

func (self *Pager) Process(context goldsmith.Context, inputFile goldsmith.File) error {
func (self *Pager) Process(context *goldsmith.Context, inputFile goldsmith.File) error {
	self.mutex.Lock()
	defer self.mutex.Unlock()



@@ 200,7 200,7 @@ func (self *Pager) Process(context goldsmith.Context, inputFile goldsmith.File) 
	return nil
}

func (self *Pager) Finalize(ctx goldsmith.Context) error {
func (self *Pager) Finalize(ctx *goldsmith.Context) error {
	for _, f := range self.files {
		ctx.DispatchFile(f)
	}

M plugins/rule/rule.go => plugins/rule/rule.go +2 -2
@@ 128,7 128,7 @@ func (*Rule) Name() string {
	return "rule"
}

func (self *Rule) Process(context goldsmith.Context, inputFile goldsmith.File) error {
func (self *Rule) Process(context *goldsmith.Context, inputFile goldsmith.File) error {
	if inputFile.Name() == self.filename {
		ruleSet, err := newRuleSet(inputFile)
		if err != nil {


@@ 147,7 147,7 @@ func (self *Rule) Process(context goldsmith.Context, inputFile goldsmith.File) e
	return nil
}

func (self *Rule) Finalize(context goldsmith.Context) error {
func (self *Rule) Finalize(context *goldsmith.Context) error {
	for _, inputFile := range self.inputFiles {
		var block bool
		for _, ruleSet := range self.ruleSets {

M plugins/summary/summary.go => plugins/summary/summary.go +2 -2
@@ 57,12 57,12 @@ func (*Summary) Name() string {
	return "summary"
}

func (*Summary) Initialize(context goldsmith.Context) error {
func (*Summary) Initialize(context *goldsmith.Context) error {
	context.Filter(wildcard.New("**/*.html", "**/*.htm"))
	return nil
}

func (self *Summary) Process(context goldsmith.Context, inputFile goldsmith.File) error {
func (self *Summary) Process(context *goldsmith.Context, inputFile goldsmith.File) error {
	doc, err := goquery.NewDocumentFromReader(inputFile)
	if err != nil {
		return err

M plugins/syndicate/syndicate.go => plugins/syndicate/syndicate.go +3 -3
@@ 120,7 120,7 @@ func (self *Syndicate) WithFeed(name string, config FeedConfig) *Syndicate {
	return self
}

func (self *Syndicate) Process(context goldsmith.Context, inputFile goldsmith.File) error {
func (self *Syndicate) Process(context *goldsmith.Context, inputFile goldsmith.File) error {
	defer context.DispatchFile(inputFile)

	getString := func(key string) string {


@@ 214,7 214,7 @@ func (self *Syndicate) Process(context goldsmith.Context, inputFile goldsmith.Fi
	return nil
}

func (self *feed) output(context goldsmith.Context) error {
func (self *feed) output(context *goldsmith.Context) error {
	feed := feeds.Feed{
		Title:       self.config.Title,
		Link:        &feeds.Link{Href: self.config.Url},


@@ 310,7 310,7 @@ func (self *feed) output(context goldsmith.Context) error {
	return nil
}

func (self *Syndicate) Finalize(context goldsmith.Context) error {
func (self *Syndicate) Finalize(context *goldsmith.Context) error {
	for _, feed := range self.feeds {
		feed.output(context)
	}

M plugins/syntax/syntax.go => plugins/syntax/syntax.go +2 -2
@@ 69,12 69,12 @@ func (*Syntax) Name() string {
	return "syntax"
}

func (*Syntax) Initialize(context goldsmith.Context) error {
func (*Syntax) Initialize(context *goldsmith.Context) error {
	context.Filter(wildcard.New("**/*.html", "**/*.htm"))
	return nil
}

func (self *Syntax) Process(context goldsmith.Context, inputFile goldsmith.File) error {
func (self *Syntax) Process(context *goldsmith.Context, inputFile goldsmith.File) error {
	if outputFile := context.RetrieveCachedFile(inputFile.Path(), inputFile); outputFile != nil {
		outputFile.CopyProps(inputFile)
		context.DispatchFile(outputFile)

M plugins/tags/tags.go => plugins/tags/tags.go +4 -4
@@ 93,12 93,12 @@ func (*Tags) Name() string {
	return "tags"
}

func (*Tags) Initialize(context goldsmith.Context) error {
func (*Tags) Initialize(context *goldsmith.Context) error {
	context.Filter(wildcard.New("**/*.html", "**/*.htm"))
	return nil
}

func (self *Tags) Process(context goldsmith.Context, inputFile goldsmith.File) error {
func (self *Tags) Process(context *goldsmith.Context, inputFile goldsmith.File) error {
	tagState := &TagState{
		TagsByName:  &self.infoByName,
		TagsByCount: &self.infoByCount,


@@ 158,7 158,7 @@ func (self *Tags) Process(context goldsmith.Context, inputFile goldsmith.File) e
	return nil
}

func (self *Tags) Finalize(context goldsmith.Context) error {
func (self *Tags) Finalize(context *goldsmith.Context) error {
	for _, info := range self.info {
		sort.Sort(info.TaggedFiles)



@@ 185,7 185,7 @@ func (self *Tags) Finalize(context goldsmith.Context) error {
	return nil
}

func (self *Tags) buildPages(context goldsmith.Context) ([]goldsmith.File, error) {
func (self *Tags) buildPages(context *goldsmith.Context) ([]goldsmith.File, error) {
	var files []goldsmith.File
	for tag, info := range self.info {
		var err error

M plugins/thumbnail/thumbnail.go => plugins/thumbnail/thumbnail.go +3 -3
@@ 76,12 76,12 @@ func (*Thumbnail) Name() string {
	return "thumbnail"
}

func (*Thumbnail) Initialize(context goldsmith.Context) error {
func (*Thumbnail) Initialize(context *goldsmith.Context) error {
	context.Filter(wildcard.New("**/*.jpg", "**/*.jpeg", "**/*.gif", "**/*.png"))
	return nil
}

func (self *Thumbnail) Process(context goldsmith.Context, inputFile goldsmith.File) error {
func (self *Thumbnail) Process(context *goldsmith.Context, inputFile goldsmith.File) error {
	defer context.DispatchFile(inputFile)

	thumbPath := self.namer(inputFile.Path(), self.size)


@@ 104,7 104,7 @@ func (self *Thumbnail) Process(context goldsmith.Context, inputFile goldsmith.Fi
	return nil
}

func (self *Thumbnail) thumbnail(context goldsmith.Context, inputFile goldsmith.File, thumbPath string) (goldsmith.File, error) {
func (self *Thumbnail) thumbnail(context *goldsmith.Context, inputFile goldsmith.File, thumbPath string) (goldsmith.File, error) {
	var thumbFormat imaging.Format
	switch strings.ToLower(filepath.Ext(thumbPath)) {
	case ".jpg", ".jpeg":

Do not follow this link