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 }