hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

chain.go (3238B)

    1 // Copyright 2018 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 transform
   15 
   16 import (
   17 	"bytes"
   18 	"io"
   19 	"io/ioutil"
   20 
   21 	bp "github.com/gohugoio/hugo/bufferpool"
   22 	"github.com/gohugoio/hugo/common/herrors"
   23 	"github.com/gohugoio/hugo/hugofs"
   24 )
   25 
   26 // Transformer is the func that needs to be implemented by a transformation step.
   27 type Transformer func(ft FromTo) error
   28 
   29 // BytesReader wraps the Bytes method, usually implemented by bytes.Buffer, and an
   30 // io.Reader.
   31 type BytesReader interface {
   32 	// The slice given by Bytes is valid for use only until the next buffer modification.
   33 	// That is, if you want to use this value outside of the current transformer step,
   34 	// you need to take a copy.
   35 	Bytes() []byte
   36 
   37 	io.Reader
   38 }
   39 
   40 // FromTo is sent to each transformation step in the chain.
   41 type FromTo interface {
   42 	From() BytesReader
   43 	To() io.Writer
   44 }
   45 
   46 // Chain is an ordered processing chain. The next transform operation will
   47 // receive the output from the previous.
   48 type Chain []Transformer
   49 
   50 // New creates a content transformer chain given the provided transform funcs.
   51 func New(trs ...Transformer) Chain {
   52 	return trs
   53 }
   54 
   55 // NewEmpty creates a new slice of transformers with a capacity of 20.
   56 func NewEmpty() Chain {
   57 	return make(Chain, 0, 20)
   58 }
   59 
   60 // Implements contentTransformer
   61 // Content is read from the from-buffer and rewritten to to the to-buffer.
   62 type fromToBuffer struct {
   63 	from *bytes.Buffer
   64 	to   *bytes.Buffer
   65 }
   66 
   67 func (ft fromToBuffer) From() BytesReader {
   68 	return ft.from
   69 }
   70 
   71 func (ft fromToBuffer) To() io.Writer {
   72 	return ft.to
   73 }
   74 
   75 // Apply passes the given from io.Reader through the transformation chain.
   76 // The result is written to to.
   77 func (c *Chain) Apply(to io.Writer, from io.Reader) error {
   78 	if len(*c) == 0 {
   79 		_, err := io.Copy(to, from)
   80 		return err
   81 	}
   82 
   83 	b1 := bp.GetBuffer()
   84 	defer bp.PutBuffer(b1)
   85 
   86 	if _, err := b1.ReadFrom(from); err != nil {
   87 		return err
   88 	}
   89 
   90 	b2 := bp.GetBuffer()
   91 	defer bp.PutBuffer(b2)
   92 
   93 	fb := &fromToBuffer{from: b1, to: b2}
   94 
   95 	for i, tr := range *c {
   96 		if i > 0 {
   97 			if fb.from == b1 {
   98 				fb.from = b2
   99 				fb.to = b1
  100 				fb.to.Reset()
  101 			} else {
  102 				fb.from = b1
  103 				fb.to = b2
  104 				fb.to.Reset()
  105 			}
  106 		}
  107 
  108 		if err := tr(fb); err != nil {
  109 			// Write output to a temp file so it can be read by the user for trouble shooting.
  110 			filename := "output.html"
  111 			tempfile, ferr := ioutil.TempFile("", "hugo-transform-error")
  112 			if ferr == nil {
  113 				filename = tempfile.Name()
  114 				defer tempfile.Close()
  115 				_, _ = io.Copy(tempfile, fb.from)
  116 				return herrors.NewFileErrorFromFile(err, filename, hugofs.Os, nil)
  117 			}
  118 			return herrors.NewFileErrorFromName(err, filename).UpdateContent(fb.from, nil)
  119 
  120 		}
  121 	}
  122 
  123 	_, err := fb.to.WriteTo(to)
  124 	return err
  125 }