Skip to content

Commit

Permalink
Merge pull request #184 from goplus/embed2
Browse files Browse the repository at this point in the history
go:embed support alias
  • Loading branch information
visualfc committed Sep 12, 2022
2 parents 80aea72 + f4d7415 commit ad152ac
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 50 deletions.
31 changes: 22 additions & 9 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,12 +265,16 @@ func (ctx *Context) loadPackage(bp *build.Package, path string, dir string) (*so
if err != nil {
return nil, err
}
if data, found := load.Embed(bp, ctx.FileSet, files, false, false); found {
embed, err := load.Embed(bp, ctx.FileSet, files, false, false)
if err != nil {
return nil, err
}
if len(embed) != 0 {
if ctx.Mode&EnableDumpEmbed != 0 {
fmt.Println("# embed", bp.Dir)
fmt.Println(string(data))
fmt.Println(string(embed))
}
f, err := parser.ParseFile(ctx.FileSet, "_igop_embed_data.go", data, 0)
f, err := parser.ParseFile(ctx.FileSet, "_igop_embed_data.go", embed, 0)
if err != nil {
return nil, err
}
Expand All @@ -294,12 +298,16 @@ func (ctx *Context) loadTestPackage(bp *build.Package, path string, dir string)
if err != nil {
return nil, err
}
if data, found := load.Embed(bp, ctx.FileSet, files, true, false); found {
embed, err := load.Embed(bp, ctx.FileSet, files, true, false)
if err != nil {
return nil, err
}
if len(embed) > 0 {
if ctx.Mode&EnableDumpEmbed != 0 {
fmt.Println("# embed", bp.Dir)
fmt.Println(string(data))
fmt.Println(string(embed))
}
f, err := parser.ParseFile(ctx.FileSet, "_igop_embed_data.go", data, 0)
f, err := parser.ParseFile(ctx.FileSet, "_igop_embed_data.go", embed, 0)
if err != nil {
return nil, err
}
Expand All @@ -317,12 +325,17 @@ func (ctx *Context) loadTestPackage(bp *build.Package, path string, dir string)
if err != nil {
return nil, err
}
if data, found := load.Embed(bp, ctx.FileSet, files, false, true); found {

embed, err := load.Embed(bp, ctx.FileSet, files, false, true)
if err != nil {
return nil, err
}
if len(embed) != 0 {
if ctx.Mode&EnableDumpEmbed != 0 {
fmt.Println("# embed xtest", bp.Dir)
fmt.Println(string(data))
fmt.Println(string(embed))
}
f, err := parser.ParseFile(ctx.FileSet, "_igop_embed_data_test.go", data, 0)
f, err := parser.ParseFile(ctx.FileSet, "_igop_embed_data_test.go", embed, 0)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ require (
github.com/peterh/liner v1.2.2
github.com/petermattis/goid v0.0.0-20220331194723-8ee3e6ded87a
github.com/visualfc/funcval v0.1.3
github.com/visualfc/goembed v0.2.2
github.com/visualfc/goembed v0.2.4
github.com/visualfc/xtype v0.1.0
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
golang.org/x/text v0.3.7
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/visualfc/funcval v0.1.3 h1:hvqP5bkW2jeA4cf7QNM8bz+duJECFzQmSBmMJkkIt3A=
github.com/visualfc/funcval v0.1.3/go.mod h1:3Izv+irhArmrTvy+lmL6pIq16gSOzx73CIka51J9eR0=
github.com/visualfc/goembed v0.2.2 h1:zmhrsIamFwtPHeVtD+ZJyfuKa3DRtjphI+160kGEnfQ=
github.com/visualfc/goembed v0.2.2/go.mod h1:jCVCz/yTJGyslo6Hta+pYxWWBuq9ADCcIVZBTQ0/iVI=
github.com/visualfc/goembed v0.2.4 h1:YIF+U6yIWZ/DLFG7t1a1Qg+tSDMWP9vISim1EGWqQbE=
github.com/visualfc/goembed v0.2.4/go.mod h1:jCVCz/yTJGyslo6Hta+pYxWWBuq9ADCcIVZBTQ0/iVI=
github.com/visualfc/xtype v0.1.0 h1:1seX3iD9v2YDORZa8Fas7CH280Fv3wulDOp2U+uPUF0=
github.com/visualfc/xtype v0.1.0/go.mod h1:VYIH9S2bmdWKlBb7c725ES6yKF9+pyHBU2SFNqGVMGM=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
Expand Down
4 changes: 2 additions & 2 deletions load/embed_go115.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ import (
"go/token"
)

func Embed(bp *build.Package, fset *token.FileSet, files []*ast.File, test bool, xtest bool) ([]byte, bool) {
return nil, false
func Embed(bp *build.Package, fset *token.FileSet, files []*ast.File, test bool, xtest bool) ([]byte, error) {
return nil, nil
}
162 changes: 131 additions & 31 deletions load/embed_go116.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,22 @@ import (
"fmt"
"go/ast"
"go/build"
"go/printer"
"go/token"
"strings"
"strconv"

"github.com/visualfc/goembed"
)

var (
__igop_embed_testdata_data2_txt__ string
)

func buildIdent(name string) string {
return fmt.Sprintf("__igop_embed_%x__", name)
}

var embed_head = `package $pkg
var embed_head = `package %v
import (
"embed"
Expand All @@ -41,8 +46,14 @@ func __igop_embed_buildFS__(list []struct {
}
`

func embedTypeError(fset *token.FileSet, spec *ast.ValueSpec) error {
var buf bytes.Buffer
printer.Fprint(&buf, fset, spec.Type)
return fmt.Errorf("%v: go:embed cannot apply to var of type %v", fset.Position(spec.Names[0].NamePos), buf.String())
}

// Embed check package embed data
func Embed(bp *build.Package, fset *token.FileSet, files []*ast.File, test bool, xtest bool) ([]byte, bool) {
func Embed(bp *build.Package, fset *token.FileSet, files []*ast.File, test bool, xtest bool) ([]byte, error) {
var pkgName string
var ems []*goembed.Embed
if xtest {
Expand All @@ -58,52 +69,141 @@ func Embed(bp *build.Package, fset *token.FileSet, files []*ast.File, test bool,
}
}
if len(ems) == 0 {
return nil, false
return nil, nil
}
r := goembed.NewResolve()
var buf bytes.Buffer
buf.WriteString(strings.Replace(embed_head, "$pkg", pkgName, 1))
buf.WriteString("\nvar (\n")
for _, v := range ems {
v.Spec.Names[0].Name = "_"
fs, _ := r.Load(bp.Dir, v)
if len(v.Spec.Values) > 0 {
return nil, fmt.Errorf("%v: go:embed cannot apply to var with initializer", v.Pos)
}
fs, err := r.Load(bp.Dir, v)
if err != nil {
return nil, err
}
switch v.Kind {
default:
switch t := v.Spec.Type.(type) {
case *ast.Ident:
// value = Type(data)
// valid alias string or []byte type used by types.check
v.Spec.Values = []ast.Expr{
&ast.CallExpr{
Fun: v.Spec.Type,
Args: []ast.Expr{
&ast.Ident{Name: buildIdent(fs[0].Name),
NamePos: v.Spec.Names[0].NamePos},
},
}}
case *ast.ArrayType:
// value = Type(data)
// valid alias string or []byte type used by types.check
if _, ok := t.Elt.(*ast.Ident); ok {
v.Spec.Values = []ast.Expr{
&ast.CallExpr{
Fun: v.Spec.Type,
Args: []ast.Expr{
&ast.Ident{Name: buildIdent(fs[0].Name),
NamePos: v.Spec.Names[0].NamePos},
},
}}
break
}
return nil, embedTypeError(fset, v.Spec)
default:
return nil, embedTypeError(fset, v.Spec)
}
case goembed.EmbedBytes:
buf.WriteString(fmt.Sprintf("\t%v = []byte(%v)\n", v.Name, buildIdent(fs[0].Name)))
// value = []byte(data)
v.Spec.Values = []ast.Expr{
&ast.CallExpr{
Fun: v.Spec.Type,
Args: []ast.Expr{ast.NewIdent(buildIdent(fs[0].Name))},
}}
case goembed.EmbedString:
buf.WriteString(fmt.Sprintf("\t%v = %v\n", v.Name, buildIdent(fs[0].Name)))
// value = data
v.Spec.Values = []ast.Expr{ast.NewIdent(buildIdent(fs[0].Name))}
case goembed.EmbedFiles:
// value = __igop_embed_buildFS__([]struct{name string; data string; hash [16]byte}{...})
fs = goembed.BuildFS(fs)
buf.WriteString(fmt.Sprintf("\t%v = ", v.Name))
buf.WriteString(`__igop_embed_buildFS__([]struct {
name string
data string
hash [16]byte
}{
`)
for _, f := range fs {
elts := make([]ast.Expr, len(fs), len(fs))
for i, f := range fs {
if len(f.Data) == 0 {
buf.WriteString(fmt.Sprintf("\t{\"%v\",\"\",[16]byte{}},\n",
f.Name))
elts[i] = &ast.CompositeLit{
Elts: []ast.Expr{
&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(f.Name)},
&ast.BasicLit{Kind: token.STRING, Value: `""`},
&ast.CompositeLit{
Type: &ast.ArrayType{
Len: &ast.BasicLit{Kind: token.INT, Value: "16"},
Elt: ast.NewIdent("byte"),
},
},
},
}
} else {
buf.WriteString(fmt.Sprintf("\t{\"%v\",%v,[16]byte{%v}},\n",
f.Name, buildIdent(f.Name), goembed.BytesToList(f.Hash[:])))
var hash [16]ast.Expr
for j, v := range f.Hash {
hash[j] = &ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(int(v))}
}
elts[i] = &ast.CompositeLit{
Elts: []ast.Expr{
&ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(f.Name)},
ast.NewIdent(buildIdent(f.Name)),
&ast.CompositeLit{
Type: &ast.ArrayType{
Len: &ast.BasicLit{Kind: token.INT, Value: "16"},
Elt: ast.NewIdent("byte"),
},
Elts: hash[:],
},
},
}
}
}
buf.WriteString("})\n")
call := &ast.CallExpr{
Fun: ast.NewIdent("__igop_embed_buildFS__"),
Args: []ast.Expr{
&ast.CompositeLit{
Type: &ast.ArrayType{
Elt: &ast.StructType{
Fields: &ast.FieldList{
List: []*ast.Field{
&ast.Field{
Names: []*ast.Ident{ast.NewIdent("name")},
Type: ast.NewIdent("string"),
},
&ast.Field{
Names: []*ast.Ident{ast.NewIdent("data")},
Type: ast.NewIdent("string"),
},
&ast.Field{
Names: []*ast.Ident{ast.NewIdent("hash")},
Type: &ast.ArrayType{
Len: &ast.BasicLit{Kind: token.INT, Value: "16"},
Elt: ast.NewIdent("byte"),
},
},
},
},
},
},
Elts: elts,
},
},
}
v.Spec.Values = []ast.Expr{call}
}
}
buf.WriteString("\n)\n")
buf.WriteString("\nvar (\n")
var buf bytes.Buffer
fmt.Fprintf(&buf, embed_head, pkgName)
buf.WriteString("\nconst (\n")
for _, f := range r.Files() {
if len(f.Data) == 0 {
buf.WriteString(fmt.Sprintf("\t%v string\n",
buildIdent(f.Name)))
fmt.Fprintf(&buf, "\t%v = \"\"\n", buildIdent(f.Name))
} else {
buf.WriteString(fmt.Sprintf("\t%v = string(\"%v\")\n",
buildIdent(f.Name), goembed.BytesToHex(f.Data)))
fmt.Fprintf(&buf, "\t%v = \"%v\"\n", buildIdent(f.Name), goembed.BytesToHex(f.Data))
}
}
buf.WriteString(")\n\n")
return buf.Bytes(), true
return buf.Bytes(), nil
}
53 changes: 48 additions & 5 deletions testdata/embed/embed.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,56 @@ package main

import (
"embed"
"fmt"
"reflect"
)

//go:embed testdata/data1.txt
var data1 string

//go:embed testdata/data2.txt
var data2 []byte
var (
//go:embed testdata/data2.txt
data2 []byte
//go:embed testdata/*
fs embed.FS
)

//go:embed testdata/*
var fs embed.FS
var (
//go:embed "testdata/data1.txt"
helloT []T
//go:embed "testdata/data1.txt"
helloUint8 []uint8
//go:embed "testdata/data1.txt"
helloEUint8 []EmbedUint8
//go:embed "testdata/data1.txt"
helloBytes EmbedBytes
//go:embed "testdata/data1.txt"
helloString EmbedString
)

func main() {
type T byte
type EmbedUint8 uint8
type EmbedBytes []byte
type EmbedString string

func checkAliases() {
want := []byte("hello data1")
check := func(g interface{}) {
got := reflect.ValueOf(g)
for i := 0; i < got.Len(); i++ {
if byte(got.Index(i).Uint()) != want[i] {
panic(fmt.Errorf("got %v want %v", got.Bytes(), want))
}
}
}
check(helloT)
check(helloUint8)
check(helloEUint8)
check(helloBytes)
check(helloString)
}

func checkEmbed() {
if data1 != "hello data1" {
panic(data1)
}
Expand All @@ -28,3 +66,8 @@ func main() {
panic(data)
}
}

func main() {
checkEmbed()
checkAliases()
}

0 comments on commit ad152ac

Please sign in to comment.