github.com/sunshinekia/hugo@v0.47.1/transform/chain.go (about)

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