我正在尝试编写一个生成代码的Go语言程序,并使用embed
包和ParseFS
来解析模板,代码最终应该满足可以从存储库中的任何目录运行的要求。
到目前为止,我已经使用ParseFiles
完成了以下工作实现。
.
├── codegen
│ └── main.go
├── foo
│ ├── foo.go
│ ├── foo.go.tmpl
│ └── foo_test.go
├── gen
│ └── foo.go
└── go.mod
foo.go
文件包含代码生成代码,
package foo
import (
"bytes"
"fmt"
"html/template"
"path/filepath"
)
const templateFile = "../foo/foo.go.tmpl"
type GeneratedType struct {
Name string
StringFields []string
}
func GenerateCode() ([]byte, error) {
tmpl, err := template.New(filepath.Base(templateFile)).ParseFiles(templateFile)
if err != nil {
return nil, fmt.Errorf("parse template: %v", err)
}
var buf bytes.Buffer
if err := tmpl.Execute(&buf, GeneratedType{
Name: "Foo",
StringFields: []string{"Bar"},
}); err != nil {
return nil, fmt.Errorf("execute template: %v", err)
}
return buf.Bytes(), nil
}
其中模板foo.go.tmpl
为
package foo
type {{.Name}} struct {
{{- range .StringFields}}
{{.}} string
{{- end}}
}
它还有一个单元测试foo_test.go
:
package foo
import (
"go/format"
"testing"
)
func TestGenerateCode(t *testing.T) {
code, err := GenerateCode()
if err != nil {
t.Errorf("generate code: %v", err)
}
if _, err := format.Source(code); err != nil {
t.Errorf("format source code: %v", err)
}
}
codegen/main.go
contains使用Go语言的generate特性运行,它包含了GenerateCode
的调用,该调用将在输出目录gen
中生成代码:
//go:generate go run github.com/khpeek/codegen-example/codegen
package main
import (
"errors"
"log"
"os"
"github.com/khpeek/codegen-example/foo"
)
func main() {
code, err := foo.GenerateCode()
if err != nil {
log.Fatalf("generate code: %v", err)
}
if err := os.Mkdir("../gen", 0700); err != nil && !errors.Is(err, os.ErrExist) {
log.Fatalf("create directory for generated code: %v", err)
}
if err := os.WriteFile("../gen/foo.go", code, 0644); err != nil {
log.Fatalf("write file: %v", err)
}
}
这是因为我可以同时调用go generate ./...
和go test ./...
来生成代码并成功运行测试。但是,它有点脆弱,因为模板文件路径../foo/foo.go.tmpl
只是"巧合地"从codegen
和foo
目录正确解析。如果我改变目录级别,我怀疑这个示例将不再工作。
我希望通过使用embed
包来使其更加健壮,这样我就可以始终引用包目录中的文件(在本例中为foo
)。为此,我尝试将foo.go
更改为:
package foo
import (
"bytes"
"embed"
"fmt"
"text/template"
)
//go:embed foo.go.tmpl
var templateFS embed.FS
type GeneratedType struct {
Name string
StringFields []string
}
func GenerateCode() ([]byte, error) {
tmpl, err := template.New("foo.go.tmpl").ParseFS(templateFS)
if err != nil {
return nil, fmt.Errorf("parse template: %v", err)
}
var buf bytes.Buffer
if err := tmpl.Execute(&buf, GeneratedType{
Name: "Foo",
StringFields: []string{"Bar"},
}); err != nil {
return nil, fmt.Errorf("execute template: %v", err)
}
return buf.Bytes(), nil
}
但是现在,当我尝试生成代码或运行单元测试时,我得到了一个no files name in call to ParseFiles
错误:
> go generate ./...
2023/01/31 09:06:21 generate code: parse template: template: no files named in call to ParseFiles
exit status 1
codegen/main.go:1: running "go": exit status 1
> go test ./...
? github.com/khpeek/codegen-example/codegen [no test files]
--- FAIL: TestGenerateCode (0.00s)
foo_test.go:11: generate code: parse template: template: no files named in call to ParseFiles
FAIL
FAIL github.com/khpeek/codegen-example/foo 0.137s
? github.com/khpeek/codegen-example/gen [no test files]
FAIL
有人能解释为什么ParseFS
没有"找到"模板文件吗?
1条答案
按热度按时间pdsfdshx1#
要将mkopriva的注解转换为答案,我需要为
ParseFS
提供第二个可变参数,表示与模板文件匹配的glob模式: