2 changed files with 88 additions and 83 deletions
@ -0,0 +1,28 @@ |
|||||
|
togo |
||||
|
==== |
||||
|
togo converts files to Go source code as a byte array. |
||||
|
togo is based on (but is a complete rewrite of) |
||||
|
[Franco Lazzarino's togo](https://github.com/flazz/togo). |
||||
|
|
||||
|
|
||||
|
usage |
||||
|
----- |
||||
|
togo is designed to be embedded in your Go project for a dependency free |
||||
|
way to manage assets. |
||||
|
|
||||
|
First, copy `main.go` from togo into a subdirectory in your |
||||
|
project (for example, a subdirectory named `togo`.) |
||||
|
|
||||
|
Next, add comments to indicate to go generate that you wish to run |
||||
|
your local copy of togo to build assets. |
||||
|
|
||||
|
//go:generate go run ./togo -n Favicon_ico -i assets/favicon.ico -p static -o static/favicon_ico.go |
||||
|
|
||||
|
togo takes some basic arguments: |
||||
|
|
||||
|
Argument | Description |
||||
|
-------- | ----------- |
||||
|
-p | generated package name |
||||
|
-n | generated variable name |
||||
|
-i | input file name |
||||
|
-o | output file name |
@ -1,111 +1,88 @@ |
|||||
package main |
package main |
||||
|
|
||||
import ( |
import ( |
||||
"bytes" |
|
||||
"flag" |
"flag" |
||||
"go/format" |
|
||||
|
"fmt" |
||||
"io" |
"io" |
||||
"io/ioutil" |
|
||||
"log" |
"log" |
||||
"os" |
"os" |
||||
"text/template" |
|
||||
|
"time" |
||||
) |
) |
||||
|
|
||||
const chunkSize = 0x10 |
|
||||
|
|
||||
const tmpl = ` |
|
||||
package {{.Pkg}} |
|
||||
|
|
||||
var {{.Name}} = []byte{ |
|
||||
// {{ len .Value }} bytes from {{ .InputPath }}
|
|
||||
{{ range .Chunks -}} |
|
||||
{{ range . }} {{ printf "0x%02x" . }}, {{ end }} |
|
||||
{{ end }} |
|
||||
} |
|
||||
` |
|
||||
|
|
||||
var t *template.Template |
|
||||
|
|
||||
func init() { |
|
||||
t = template.Must(template.New("constfile").Parse(tmpl)) |
|
||||
} |
|
||||
|
|
||||
type file struct { |
|
||||
Pkg, Name, InputPath string |
|
||||
Value []byte |
|
||||
} |
|
||||
|
|
||||
func (f *file) Chunks() [][]byte { |
|
||||
return chunks(f.Value, chunkSize) |
|
||||
} |
|
||||
|
|
||||
func chunks(b []byte, n int) [][]byte { |
|
||||
var c [][]byte |
|
||||
|
|
||||
nChks := len(b) / n |
|
||||
|
|
||||
for i := 0; i < nChks; i++ { |
|
||||
m := i * n |
|
||||
c = append(c, b[m:m+n]) |
|
||||
} |
|
||||
|
|
||||
if r := len(b) % n; r > 0 { |
|
||||
m := n * nChks |
|
||||
c = append(c, b[m:m+r]) |
|
||||
} |
|
||||
|
|
||||
return c |
|
||||
} |
|
||||
|
|
||||
func (f *file) Read() (err error) { |
|
||||
f.Value, err = ioutil.ReadFile(f.InputPath) |
|
||||
return |
|
||||
} |
|
||||
|
func main() { |
||||
|
pkg := flag.String("p", "", "package") |
||||
|
name := flag.String("n", "", "const name") |
||||
|
inputfn := flag.String("i", "", "input file") |
||||
|
outputfn := flag.String("o", "", "output file") |
||||
|
flag.Parse() |
||||
|
|
||||
func (f *file) Render(w io.Writer) error { |
|
||||
outputPath := f.InputPath + ".go" |
|
||||
var buf bytes.Buffer |
|
||||
if err := t.Execute(&buf, &f); err != nil { |
|
||||
return err |
|
||||
|
if *pkg == "" { |
||||
|
log.Fatal("pkg required") |
||||
} |
} |
||||
|
|
||||
b, err := format.Source(buf.Bytes()) |
|
||||
if err != nil { |
|
||||
log.Fatal(err) |
|
||||
|
if *name == "" { |
||||
|
log.Fatal("name required") |
||||
} |
} |
||||
|
|
||||
if err := ioutil.WriteFile(outputPath, b, os.ModePerm); err != nil { |
|
||||
return err |
|
||||
|
if *inputfn == "" { |
||||
|
log.Fatal("input file required") |
||||
} |
} |
||||
|
|
||||
return nil |
|
||||
} |
|
||||
|
|
||||
func main() { |
|
||||
|
|
||||
var f file |
|
||||
flag.StringVar(&f.Pkg, "pkg", "", "package") |
|
||||
flag.StringVar(&f.Name, "name", "", "const name") |
|
||||
flag.StringVar(&f.InputPath, "input", "", "input file") |
|
||||
flag.Parse() |
|
||||
|
|
||||
if f.Pkg == "" { |
|
||||
log.Fatal("pkg required") |
|
||||
|
if *outputfn == "" { |
||||
|
*outputfn = *inputfn + ".go" |
||||
} |
} |
||||
|
|
||||
if f.Name == "" { |
|
||||
log.Fatal("name required") |
|
||||
|
omod := fmod(*outputfn) |
||||
|
imod := fmod(*inputfn) |
||||
|
if omod.After(imod) { |
||||
|
log.Printf("Refusing to update %s\n", *outputfn) |
||||
|
return |
||||
} |
} |
||||
|
|
||||
if f.InputPath == "" { |
|
||||
log.Fatal("input file required") |
|
||||
|
ifile, err := os.Open(*inputfn) |
||||
|
if err != nil { |
||||
|
log.Fatal(err) |
||||
} |
} |
||||
|
defer ifile.Close() |
||||
|
|
||||
if err := f.Read(); err != nil { |
|
||||
|
ofile, err := os.OpenFile(*outputfn, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0660) |
||||
|
if err != nil { |
||||
log.Fatal(err) |
log.Fatal(err) |
||||
} |
} |
||||
|
defer ofile.Close() |
||||
|
|
||||
|
fmt.Fprintf(ofile, "package %s\n\nvar %s = []byte{", *pkg, *name) |
||||
|
|
||||
|
buf := make([]byte, 4096) |
||||
|
for c := 0; ; { |
||||
|
i, err := ifile.Read(buf) |
||||
|
if err != nil { |
||||
|
if err != io.EOF { |
||||
|
log.Fatal(err) |
||||
|
} |
||||
|
break |
||||
|
} |
||||
|
|
||||
|
for j := 0; j < i; j++ { |
||||
|
if (c % 13) == 0 { |
||||
|
fmt.Fprintf(ofile, "\n\t") |
||||
|
} else { |
||||
|
fmt.Fprintf(ofile, " ") |
||||
|
} |
||||
|
fmt.Fprintf(ofile, "0x%02x,", buf[j]) |
||||
|
c++ |
||||
|
} |
||||
|
} |
||||
|
fmt.Fprintf(ofile, "\n}\n") |
||||
|
} |
||||
|
|
||||
if err := f.Render(os.Stdout); err != nil { |
|
||||
|
func fmod(fn string) time.Time { |
||||
|
fi, err := os.Stat(fn) |
||||
|
if err != nil { |
||||
|
if os.IsNotExist(err) { |
||||
|
return time.Time{} |
||||
|
} |
||||
log.Fatal(err) |
log.Fatal(err) |
||||
} |
} |
||||
|
return fi.ModTime() |
||||
} |
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue