complete rewrite, added basic documentation
This commit is contained in:
parent
1d10f488b0
commit
0bfc3ce4b0
28
README.md
28
README.md
@ -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
147
main.go
@ -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()
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user