Archived
1
0

complete rewrite, added basic documentation

This commit is contained in:
Christopher Ramey 2019-12-28 10:23:14 -09:00 committed by Christopher Ramey
parent 1d10f488b0
commit 0bfc3ce4b0
2 changed files with 90 additions and 85 deletions

View File

@ -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

147
main.go
View File

@ -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 (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
}
b, err := format.Source(buf.Bytes())
if err != nil {
log.Fatal(err)
}
if err := ioutil.WriteFile(outputPath, b, os.ModePerm); err != nil {
return err
}
return nil
}
func main() { func main() {
pkg := flag.String("p", "", "package")
var f file name := flag.String("n", "", "const name")
flag.StringVar(&f.Pkg, "pkg", "", "package") inputfn := flag.String("i", "", "input file")
flag.StringVar(&f.Name, "name", "", "const name") outputfn := flag.String("o", "", "output file")
flag.StringVar(&f.InputPath, "input", "", "input file")
flag.Parse() flag.Parse()
if f.Pkg == "" { if *pkg == "" {
log.Fatal("pkg required") log.Fatal("pkg required")
} }
if f.Name == "" { if *name == "" {
log.Fatal("name required") log.Fatal("name required")
} }
if f.InputPath == "" { if *inputfn == "" {
log.Fatal("input file required") log.Fatal("input file required")
} }
if err := f.Read(); err != nil { if *outputfn == "" {
log.Fatal(err) *outputfn = *inputfn + ".go"
} }
if err := f.Render(os.Stdout); err != nil { omod := fmod(*outputfn)
imod := fmod(*inputfn)
if omod.After(imod) {
log.Printf("Refusing to update %s\n", *outputfn)
return
}
ifile, err := os.Open(*inputfn)
if err != nil {
log.Fatal(err) log.Fatal(err)
} }
defer ifile.Close()
ofile, err := os.OpenFile(*outputfn, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0660)
if err != nil {
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")
}
func fmod(fn string) time.Time {
fi, err := os.Stat(fn)
if err != nil {
if os.IsNotExist(err) {
return time.Time{}
}
log.Fatal(err)
}
return fi.ModTime()
} }