github.com/dahs81/otto@v0.2.1-0.20160126165905-6400716cf085/helper/bindata/data.go (about)

     1  package bindata
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"log"
     9  	"os"
    10  	"path/filepath"
    11  	"strings"
    12  
    13  	"github.com/flosch/pongo2"
    14  
    15  	// Template helpers
    16  	_ "github.com/hashicorp/otto/helper/pongo2"
    17  )
    18  
    19  //go:generate go-bindata -o=bindata_test.go -pkg=bindata -nomemcopy -nometadata ./test-data/...
    20  
    21  // Data is the struct that wraps the assets go-bindata generates in your
    22  // package to provide more helpers.
    23  type Data struct {
    24  	// Asset, AssetDir are functions used to look up the assets.
    25  	// These match the function signatures used by go-bindata so you
    26  	// can just use method handles for these.
    27  	Asset    func(string) ([]byte, error)
    28  	AssetDir func(string) ([]string, error)
    29  
    30  	// Context is the template context that is given when rendering
    31  	Context map[string]interface{}
    32  
    33  	// SharedExtends is a mapping of share prefixes and files that can be
    34  	// accessed using {% extends %} in templates. Example:
    35  	// {% extends "foo:bar/baz.tpl" %} would find the "bar/baz.tpl" in the
    36  	// "foo" share.
    37  	SharedExtends map[string]*Data
    38  }
    39  
    40  // CopyDir copies all the assets from the given prefix to the destination
    41  // directory. It will automatically set file permissions, create folders,
    42  // etc.
    43  func (d *Data) CopyDir(dst, prefix string) error {
    44  	return d.copyDir(dst, prefix)
    45  }
    46  
    47  // RenderAsset renders a single bindata asset. This file
    48  // will be processed as a template if it ends in ".tpl".
    49  func (d *Data) RenderAsset(dst, src string) error {
    50  	data, err := d.Asset(src)
    51  	if err != nil {
    52  		return err
    53  	}
    54  
    55  	return d.renderLowLevel(dst, src, "", bytes.NewReader(data))
    56  }
    57  
    58  // RenderReal renders a real file (not a bindata'd file). This file
    59  // will be processed as a template if it ends in ".tpl".
    60  func (d *Data) RenderReal(dst, src string) error {
    61  	f, err := os.Open(src)
    62  	if err != nil {
    63  		return err
    64  	}
    65  	defer f.Close()
    66  
    67  	return d.renderLowLevel(dst, src, "", f)
    68  }
    69  
    70  // RenderString renders a string.
    71  func (d *Data) RenderString(tpl string) (string, error) {
    72  	// Make a temporary file for the contents. This is kind of silly we
    73  	// need to do this but we can make this render in-memory later.
    74  	tf, err := ioutil.TempFile("", "otto")
    75  	if err != nil {
    76  		return "", err
    77  	}
    78  	tf.Close()
    79  	defer os.Remove(tf.Name())
    80  
    81  	// Render
    82  	err = d.renderLowLevel(tf.Name(), "dummy.tpl", "", strings.NewReader(tpl))
    83  	if err != nil {
    84  		return "", err
    85  	}
    86  
    87  	// Copy the file contents back into memory
    88  	var result bytes.Buffer
    89  	f, err := os.Open(tf.Name())
    90  	if err != nil {
    91  		return "", err
    92  	}
    93  	_, err = io.Copy(&result, f)
    94  	f.Close()
    95  	if err != nil {
    96  		return "", err
    97  	}
    98  
    99  	return result.String(), nil
   100  }
   101  
   102  func (d *Data) copyDir(dst, prefix string) error {
   103  	log.Printf("[DEBUG] Copying all assets: %s => %s", prefix, dst)
   104  
   105  	// Get all the assets in the directory
   106  	assets, err := d.AssetDir(prefix)
   107  	if err != nil {
   108  		return err
   109  	}
   110  
   111  	// If the destination directory doesn't exist, make that
   112  	if err := os.MkdirAll(dst, 0755); err != nil {
   113  		return err
   114  	}
   115  
   116  	// Go through each asset, and copy it into place
   117  	for _, asset := range assets {
   118  		log.Printf("[DEBUG] Copying asset: %s", asset)
   119  
   120  		// Load the asset bytes
   121  		assetFull := prefix + "/" + asset
   122  		data, err := d.Asset(assetFull)
   123  		if err != nil {
   124  			// Asset not found... check if it is a directory. If it is
   125  			// a directory, we recursively call CopyDir.
   126  			if _, err := d.AssetDir(assetFull); err != nil {
   127  				return fmt.Errorf("error loading asset %s: %s", asset, err)
   128  			}
   129  
   130  			if err := d.copyDir(filepath.Join(dst, asset), assetFull); err != nil {
   131  				return err
   132  			}
   133  
   134  			continue
   135  		}
   136  
   137  		err = d.renderLowLevel(filepath.Join(dst, asset), asset, prefix, bytes.NewReader(data))
   138  		if err != nil {
   139  			return err
   140  		}
   141  	}
   142  
   143  	return nil
   144  }
   145  
   146  func (d *Data) renderLowLevel(dst string, src string, prefix string, r io.Reader) error {
   147  	var err error
   148  
   149  	// Determine the filename and whether we're dealing with a template
   150  	var tpl *pongo2.Template = nil
   151  	filename := src
   152  	if strings.HasSuffix(filename, ".tpl") {
   153  		var buf bytes.Buffer
   154  		if _, err := io.Copy(&buf, r); err != nil {
   155  			return err
   156  		}
   157  
   158  		base := filepath.Dir(filename)
   159  		if prefix != "" {
   160  			base = filepath.Join(prefix, base)
   161  		}
   162  
   163  		// Create the template set so we can control loading
   164  		tplSet := pongo2.NewSet("otto", &tplLoader{
   165  			Data: d,
   166  			Base: base,
   167  		})
   168  
   169  		// Parse the template
   170  		dst = strings.TrimSuffix(dst, ".tpl")
   171  		tpl, err = tplSet.FromString(buf.String())
   172  		if err != nil {
   173  			return err
   174  		}
   175  	}
   176  
   177  	// Make the directory containing the final path.
   178  	dir := filepath.Dir(dst)
   179  	if err := os.MkdirAll(dir, 0755); err != nil {
   180  		return err
   181  	}
   182  
   183  	// Create the file itself
   184  	f, err := os.Create(dst)
   185  	if err != nil {
   186  		return err
   187  	}
   188  	defer f.Close()
   189  
   190  	// If it isn't a template, do a direct byte copy
   191  	if tpl == nil {
   192  		_, err = io.Copy(f, r)
   193  		return err
   194  	}
   195  
   196  	return tpl.ExecuteWriter(d.Context, f)
   197  }